gofront 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +2 -2
- package/src/backend/js/expressions.js +329 -58
- package/src/backend/js/index.js +90 -10
- package/src/backend/js/runtime.js +51 -9
- package/src/backend/js/statements.js +193 -84
- package/src/backend/js/type-info.js +123 -8
- package/src/backend/shared/fmt-types.js +30 -1
- package/src/backend/wasm/emit-builtins.js +250 -9
- package/src/backend/wasm/emit-exprs.js +63 -22
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +239 -205
- package/src/backend/wasm/emit.js +73 -26
- package/src/backend/wasm/globals.js +1 -0
- package/src/backend/wasm/index.js +8 -0
- package/src/backend/wasm/module-emitter.js +211 -57
- package/src/backend/wasm/monomorph.js +36 -2
- package/src/backend/wasm/natives.js +4 -0
- package/src/backend/wasm/types.js +32 -3
- package/src/backend/wasm/variadic.js +6 -1
- package/src/compiler.js +6 -1
- package/src/lexer/lexer.js +1 -0
- package/src/link/stdlib.js +1 -0
- package/src/link/type-names.js +72 -16
- package/src/lower/embedding.js +43 -15
- package/src/lower/fmt-formatters.js +38 -14
- package/src/lower/functions.js +223 -7
- package/src/lower/gen-types.js +21 -2
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +114 -6
- package/src/parser/declarations.js +4 -2
- package/src/parser/expressions.js +25 -41
- package/src/parser/statements.js +107 -0
- package/src/parser/types.js +11 -0
- package/src/typechecker/assignability.js +94 -12
- package/src/typechecker/constants.js +124 -2
- package/src/typechecker/expressions.js +171 -17
- package/src/typechecker/index.js +158 -29
- package/src/typechecker/operand-text.js +1 -0
- package/src/typechecker/resolve.js +308 -39
- package/src/typechecker/statements.js +171 -21
- package/src/typechecker/types.js +84 -2
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
is64BitInt,
|
|
15
15
|
isComplex,
|
|
16
|
+
isComplex64,
|
|
16
17
|
isTypedArraySlice,
|
|
17
18
|
} from "../../typechecker/types.js";
|
|
18
19
|
|
|
@@ -421,10 +422,25 @@ export const statementGenMethods = {
|
|
|
421
422
|
labels.push(body.label);
|
|
422
423
|
body = body.body;
|
|
423
424
|
}
|
|
424
|
-
if (body?.kind === "ForStmt")
|
|
425
|
+
if (body?.kind === "ForStmt") {
|
|
426
|
+
body.label = labels[labels.length - 1];
|
|
427
|
+
body.labels = labels;
|
|
428
|
+
}
|
|
425
429
|
const hoisted = this._hoistForInit(body);
|
|
426
430
|
for (const label of labels) this.line(`${label}:`);
|
|
427
|
-
|
|
431
|
+
// Labels in scope of the JS function being emitted (see
|
|
432
|
+
// `_emitIterTargetDispatch`).
|
|
433
|
+
const prevLabels = this._jsLabels ?? [];
|
|
434
|
+
const loop = body?.kind === "ForStmt";
|
|
435
|
+
this._jsLabels = [
|
|
436
|
+
...prevLabels,
|
|
437
|
+
...labels.map((name) => ({ name, loop })),
|
|
438
|
+
];
|
|
439
|
+
try {
|
|
440
|
+
this.genStmt(body);
|
|
441
|
+
} finally {
|
|
442
|
+
this._jsLabels = prevLabels;
|
|
443
|
+
}
|
|
428
444
|
if (hoisted) this._closeHoistedForInit();
|
|
429
445
|
break;
|
|
430
446
|
}
|
|
@@ -767,6 +783,14 @@ export const statementGenMethods = {
|
|
|
767
783
|
if (syncs) for (const s of syncs) this.line(`${s};`);
|
|
768
784
|
},
|
|
769
785
|
|
|
786
|
+
// An untyped constant operand of a 64-bit compound assignment is a Number
|
|
787
|
+
// in the output; the operator needs it as a BigInt.
|
|
788
|
+
_untypedTo64(rhsVal, rhsNode) {
|
|
789
|
+
if (rhsNode?._type?.kind !== "untyped" || rhsVal.endsWith("n"))
|
|
790
|
+
return rhsVal;
|
|
791
|
+
return /^-?\d+$/.test(rhsVal) ? `${rhsVal}n` : `BigInt(${rhsVal})`;
|
|
792
|
+
},
|
|
793
|
+
|
|
770
794
|
_genAssignStmt(stmt) {
|
|
771
795
|
this._markIndexLvalues(stmt.lhs);
|
|
772
796
|
if (stmt.op !== "=") for (const l of stmt.lhs) this._markDerefOpTarget(l);
|
|
@@ -841,14 +865,7 @@ export const statementGenMethods = {
|
|
|
841
865
|
const binOp = stmt.op.slice(0, -1);
|
|
842
866
|
const is64Val = is64BitInt(stmt.lhs[0]._mapValueType);
|
|
843
867
|
let rhsVal = active[0].r;
|
|
844
|
-
if (
|
|
845
|
-
is64Val &&
|
|
846
|
-
stmt.rhs[0]?._type?.kind === "untyped" &&
|
|
847
|
-
!rhsVal.endsWith("n") &&
|
|
848
|
-
/^-?\d+$/.test(rhsVal)
|
|
849
|
-
) {
|
|
850
|
-
rhsVal = `${rhsVal}n`;
|
|
851
|
-
}
|
|
868
|
+
if (is64Val) rhsVal = this._untypedTo64(rhsVal, stmt.rhs[0]);
|
|
852
869
|
const rhsExpr = is64Val
|
|
853
870
|
? this._gen64BitBinary(
|
|
854
871
|
binOp,
|
|
@@ -881,14 +898,7 @@ export const statementGenMethods = {
|
|
|
881
898
|
}
|
|
882
899
|
const is64 = is64BitInt(stmt.lhs[0]._type);
|
|
883
900
|
let rhsVal = active[0].r;
|
|
884
|
-
if (
|
|
885
|
-
is64 &&
|
|
886
|
-
stmt.rhs[0]?._type?.kind === "untyped" &&
|
|
887
|
-
!rhsVal.endsWith("n") &&
|
|
888
|
-
/^-?\d+$/.test(rhsVal)
|
|
889
|
-
) {
|
|
890
|
-
rhsVal = `${rhsVal}n`;
|
|
891
|
-
}
|
|
901
|
+
if (is64) rhsVal = this._untypedTo64(rhsVal, stmt.rhs[0]);
|
|
892
902
|
if (
|
|
893
903
|
this._needsStrictCompoundStmt(stmt, is64) &&
|
|
894
904
|
this._genStrictCompound(
|
|
@@ -1134,27 +1144,16 @@ export const statementGenMethods = {
|
|
|
1134
1144
|
}
|
|
1135
1145
|
const rhsExpr = this._genComplexOperand(stmt.rhs[0]);
|
|
1136
1146
|
const baseOp = stmt.op.slice(0, -1); // "+=" → "+"
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
result = `__cmul(${lhsRead}, ${rhsExpr})`;
|
|
1148
|
-
break;
|
|
1149
|
-
case "/":
|
|
1150
|
-
this.useHelper("cdiv");
|
|
1151
|
-
result = `__cdiv(${lhsRead}, ${rhsExpr})`;
|
|
1152
|
-
break;
|
|
1153
|
-
default:
|
|
1154
|
-
throw new Error(
|
|
1155
|
-
`unsupported complex compound assignment operator: ${stmt.op}`,
|
|
1156
|
-
);
|
|
1157
|
-
}
|
|
1147
|
+
if (!["+", "-", "*", "/"].includes(baseOp))
|
|
1148
|
+
throw new Error(
|
|
1149
|
+
`unsupported complex compound assignment operator: ${stmt.op}`,
|
|
1150
|
+
);
|
|
1151
|
+
const result = this._genComplexArith(
|
|
1152
|
+
baseOp,
|
|
1153
|
+
lhsRead,
|
|
1154
|
+
rhsExpr,
|
|
1155
|
+
isComplex64(l._type),
|
|
1156
|
+
);
|
|
1158
1157
|
this.line(`${lhsTarget} = ${result};`);
|
|
1159
1158
|
},
|
|
1160
1159
|
|
|
@@ -1163,10 +1162,21 @@ export const statementGenMethods = {
|
|
|
1163
1162
|
this.line(`${this._iterReturnFlag} = true; return false;`);
|
|
1164
1163
|
return;
|
|
1165
1164
|
}
|
|
1166
|
-
|
|
1165
|
+
let vals =
|
|
1167
1166
|
stmt.values.length > 0
|
|
1168
1167
|
? stmt.values.map((v) => this._genReturnValue(v))
|
|
1169
1168
|
: (this.namedReturnVars ?? []);
|
|
1169
|
+
const named = this.namedReturnVars ?? [];
|
|
1170
|
+
if (stmt.values.length > 0 && named.length > 0) {
|
|
1171
|
+
// Deferred calls read the named results after the loop returns.
|
|
1172
|
+
const rhs =
|
|
1173
|
+
vals.length === named.length && named.length > 1
|
|
1174
|
+
? `[${vals.join(", ")}]`
|
|
1175
|
+
: vals[0];
|
|
1176
|
+
const lhs = named.length === 1 ? named[0] : `[${named.join(", ")}]`;
|
|
1177
|
+
this.line(`${lhs} = ${rhs};`);
|
|
1178
|
+
vals = named;
|
|
1179
|
+
}
|
|
1170
1180
|
const stored = vals.length === 1 ? vals[0] : `[${vals.join(", ")}]`;
|
|
1171
1181
|
this.line(
|
|
1172
1182
|
`${this._iterReturnFlag} = true; ${this._iterReturnVar} = ${stored}; return false;`,
|
|
@@ -1235,11 +1245,10 @@ export const statementGenMethods = {
|
|
|
1235
1245
|
this.line(`${stmt.keyword} ${stmt.label};`);
|
|
1236
1246
|
return;
|
|
1237
1247
|
}
|
|
1238
|
-
if (
|
|
1239
|
-
stmt.keyword === "continue"
|
|
1240
|
-
stmt.
|
|
1241
|
-
|
|
1242
|
-
this.line("return true;");
|
|
1248
|
+
if (this._currentIterLoopLabels?.includes(stmt.label)) {
|
|
1249
|
+
if (stmt.keyword === "continue") this.line("return true;");
|
|
1250
|
+
else if (stmt.keyword === "break")
|
|
1251
|
+
this.line(`${this._iterBreakFlag} = true; return false;`);
|
|
1243
1252
|
return;
|
|
1244
1253
|
}
|
|
1245
1254
|
this.line(
|
|
@@ -1360,10 +1369,8 @@ export const statementGenMethods = {
|
|
|
1360
1369
|
const isInner = this._inIteratorBody;
|
|
1361
1370
|
if (isInner) {
|
|
1362
1371
|
this._iterLoopDepth = (this._iterLoopDepth ?? 0) + 1;
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
this._innerLoopLabels.add(stmt.label);
|
|
1366
|
-
}
|
|
1372
|
+
this._innerLoopLabels = this._innerLoopLabels ?? new Set();
|
|
1373
|
+
for (const l of this._loopLabels(stmt)) this._innerLoopLabels.add(l);
|
|
1367
1374
|
}
|
|
1368
1375
|
try {
|
|
1369
1376
|
if (this.isRangeFor(stmt)) {
|
|
@@ -1391,7 +1398,7 @@ export const statementGenMethods = {
|
|
|
1391
1398
|
} finally {
|
|
1392
1399
|
if (isInner) {
|
|
1393
1400
|
this._iterLoopDepth--;
|
|
1394
|
-
|
|
1401
|
+
for (const l of this._loopLabels(stmt)) this._innerLoopLabels.delete(l);
|
|
1395
1402
|
}
|
|
1396
1403
|
}
|
|
1397
1404
|
},
|
|
@@ -1447,10 +1454,26 @@ export const statementGenMethods = {
|
|
|
1447
1454
|
return `${this._genAssignLhsExpr(e)} = `;
|
|
1448
1455
|
},
|
|
1449
1456
|
|
|
1457
|
+
// The value a range assignment stores into an interface-typed variable,
|
|
1458
|
+
// boxed as in any conversion to an interface.
|
|
1459
|
+
_boxRangeAssigned(e, valueJs) {
|
|
1460
|
+
const box = e._rangeBox;
|
|
1461
|
+
if (!box) return valueJs;
|
|
1462
|
+
if (this._isNamedBoxKind(box))
|
|
1463
|
+
return this._genNamedIfaceBox(
|
|
1464
|
+
valueJs,
|
|
1465
|
+
box._ifaceNamedType,
|
|
1466
|
+
box._ifaceBox === "named_ptr",
|
|
1467
|
+
);
|
|
1468
|
+
this.useHelper("ifaceBox");
|
|
1469
|
+
return this._ifaceBoxWrap(valueJs, box._ifaceBox, box._ifacePtrFields);
|
|
1470
|
+
},
|
|
1471
|
+
|
|
1450
1472
|
_emitRangeVar(e, valueJs, isAssign) {
|
|
1451
1473
|
if (!e) return;
|
|
1452
1474
|
const target = this._rangeVarTarget(e, isAssign);
|
|
1453
1475
|
if (target === null) return;
|
|
1476
|
+
if (isAssign) valueJs = this._boxRangeAssigned(e, valueJs);
|
|
1454
1477
|
if (typeof target === "string") {
|
|
1455
1478
|
this.line(`${target}${valueJs};`);
|
|
1456
1479
|
return;
|
|
@@ -1605,13 +1628,38 @@ export const statementGenMethods = {
|
|
|
1605
1628
|
this._loopDepth--;
|
|
1606
1629
|
},
|
|
1607
1630
|
|
|
1608
|
-
|
|
1631
|
+
// `for _, v = range m` into an address-taken variable (or an element or
|
|
1632
|
+
// field) cannot destructure into the target: bind temporaries and store
|
|
1633
|
+
// through the variable's box after decoding. Fills `stores` with the
|
|
1634
|
+
// [target, temporary] pairs and returns the names to bind.
|
|
1635
|
+
_rangeAssignTemps(init, names, stores) {
|
|
1636
|
+
if (init.kind !== "AssignStmt") return names;
|
|
1637
|
+
// `let [..]` declares every name, so once one target needs a temporary
|
|
1638
|
+
// all the others go through temporaries too.
|
|
1639
|
+
const needs = (n, i) => {
|
|
1640
|
+
const e = init.lhs[i];
|
|
1641
|
+
return (
|
|
1642
|
+
n !== "_" &&
|
|
1643
|
+
(e._rangeBox || !(e.kind === "Ident" && !this._boxedVars.has(e.name)))
|
|
1644
|
+
);
|
|
1645
|
+
};
|
|
1646
|
+
const any = names.some(needs);
|
|
1647
|
+
return names.map((n, i) => {
|
|
1648
|
+
if (n === "_" || !any) return n;
|
|
1649
|
+
stores.push([init.lhs[i], `__r${i}$`]);
|
|
1650
|
+
return `__r${i}$`;
|
|
1651
|
+
});
|
|
1652
|
+
},
|
|
1653
|
+
|
|
1654
|
+
_genIterFor(init, iterType, iteree, lhsNames, body) {
|
|
1655
|
+
const isAssign = init.kind === "AssignStmt";
|
|
1656
|
+
const stores = [];
|
|
1657
|
+
const lhs = this._rangeAssignTemps(init, lhsNames, stores);
|
|
1609
1658
|
const iterExpr = this._genRangeIterExpr(iterType, iteree, lhs);
|
|
1610
1659
|
const keyDecode = this._structKeyRange(iterType);
|
|
1611
1660
|
const paramKey = this._typeParamKeyRange(iterType);
|
|
1612
1661
|
const is64Key = this._is64BitMapRange(iterType);
|
|
1613
1662
|
const scalarKey = this._scalarKeyRange(iterType);
|
|
1614
|
-
const isAssign = init.kind === "AssignStmt";
|
|
1615
1663
|
// `for _, v = range m` assigns existing variables, where a blank is a
|
|
1616
1664
|
// hole in the destructuring pattern; a lone blank still needs a binding.
|
|
1617
1665
|
const blank = isAssign && lhs.length > 1 ? "" : "_$";
|
|
@@ -1621,7 +1669,7 @@ export const statementGenMethods = {
|
|
|
1621
1669
|
? "_$"
|
|
1622
1670
|
: lhs[0]
|
|
1623
1671
|
: `[${lhs.map((n) => (n === "_" ? blank : n)).join(", ")}]`;
|
|
1624
|
-
const kw = !isAssign || binding === "_$" ? "let " : "";
|
|
1672
|
+
const kw = !isAssign || binding === "_$" || stores.length > 0 ? "let " : "";
|
|
1625
1673
|
this.line(`for (${kw}${binding} of ${iterExpr}) {`);
|
|
1626
1674
|
this.indented(() => {
|
|
1627
1675
|
if (paramKey && lhs[0] !== "_")
|
|
@@ -1637,6 +1685,7 @@ export const statementGenMethods = {
|
|
|
1637
1685
|
: `${lhs[0]} = Number(${lhs[0]});`,
|
|
1638
1686
|
);
|
|
1639
1687
|
this._cloneMapRangeValue(init, iterType, lhs[1]);
|
|
1688
|
+
for (const [e, tmp] of stores) this._emitRangeVar(e, tmp, true);
|
|
1640
1689
|
this.genBlock(body);
|
|
1641
1690
|
});
|
|
1642
1691
|
this.line("}");
|
|
@@ -1775,17 +1824,40 @@ export const statementGenMethods = {
|
|
|
1775
1824
|
return `__s(${iteree}).entries()`;
|
|
1776
1825
|
},
|
|
1777
1826
|
|
|
1778
|
-
|
|
1779
|
-
|
|
1827
|
+
// Every label of a loop: stacked labels (`a: b: for ...`) all name it.
|
|
1828
|
+
_loopLabels(stmt) {
|
|
1829
|
+
return stmt.labels ?? (stmt.label ? [stmt.label] : []);
|
|
1830
|
+
},
|
|
1831
|
+
|
|
1832
|
+
_emitIterTargetDispatch(targetFlag, ownLabels = []) {
|
|
1833
|
+
// The loop's own label is handled inside the callback (`break` and
|
|
1834
|
+
// `continue` of the range-over-func loop), and a JS `continue` cannot
|
|
1835
|
+
// name the labeled block that wraps it. Only the labels of statements
|
|
1836
|
+
// enclosing this loop in the same JS function are in scope (`_jsLabels`):
|
|
1837
|
+
// inside another iterator's callback, the enclosing iterator loop's
|
|
1838
|
+
// label is handled like a branch in its body, and any other label is
|
|
1839
|
+
// passed on to the next dispatcher.
|
|
1840
|
+
const inIter = this._inIteratorBody;
|
|
1841
|
+
const labels = (this._jsLabels ?? []).filter(
|
|
1842
|
+
(l) => !ownLabels.includes(l.name),
|
|
1843
|
+
);
|
|
1780
1844
|
this.line(`if (${targetFlag}) {`);
|
|
1781
1845
|
this.indented(() => {
|
|
1782
|
-
for (const
|
|
1783
|
-
this.line(`if (${targetFlag} === "break$${
|
|
1784
|
-
|
|
1846
|
+
for (const { name, loop } of labels) {
|
|
1847
|
+
this.line(`if (${targetFlag} === "break$${name}") break ${name};`);
|
|
1848
|
+
if (loop)
|
|
1849
|
+
this.line(
|
|
1850
|
+
`if (${targetFlag} === "continue$${name}") continue ${name};`,
|
|
1851
|
+
);
|
|
1785
1852
|
}
|
|
1786
|
-
if (
|
|
1787
|
-
|
|
1853
|
+
if (!inIter) return;
|
|
1854
|
+
for (const encl of this._currentIterLoopLabels ?? []) {
|
|
1855
|
+
this.line(`if (${targetFlag} === "continue$${encl}") return true;`);
|
|
1856
|
+
this.line(
|
|
1857
|
+
`if (${targetFlag} === "break$${encl}") { ${this._iterBreakFlag} = true; return false; }`,
|
|
1858
|
+
);
|
|
1788
1859
|
}
|
|
1860
|
+
this.line(`${this._iterTargetFlag} = ${targetFlag}; return false;`);
|
|
1789
1861
|
});
|
|
1790
1862
|
this.line("}");
|
|
1791
1863
|
},
|
|
@@ -1795,7 +1867,7 @@ export const statementGenMethods = {
|
|
|
1795
1867
|
paramsStr,
|
|
1796
1868
|
body,
|
|
1797
1869
|
assignPreamble = null,
|
|
1798
|
-
|
|
1870
|
+
loopLabels = [],
|
|
1799
1871
|
) {
|
|
1800
1872
|
let iteree = this.genExpr(itereeExpr);
|
|
1801
1873
|
if (itereeExpr.kind === "FuncLit") {
|
|
@@ -1834,16 +1906,21 @@ export const statementGenMethods = {
|
|
|
1834
1906
|
ret: this._iterReturnFlag,
|
|
1835
1907
|
retVar: this._iterReturnVar,
|
|
1836
1908
|
target: this._iterTargetFlag,
|
|
1837
|
-
|
|
1909
|
+
loopLabels: this._currentIterLoopLabels,
|
|
1838
1910
|
loopDepth: this._iterLoopDepth,
|
|
1911
|
+
innerLabels: this._innerLoopLabels,
|
|
1912
|
+
jsLabels: this._jsLabels,
|
|
1839
1913
|
};
|
|
1840
1914
|
this._inIteratorBody = true;
|
|
1841
1915
|
this._iterBreakFlag = breakFlag;
|
|
1842
1916
|
this._iterReturnFlag = retFlag;
|
|
1843
1917
|
this._iterReturnVar = retVar;
|
|
1844
1918
|
this._iterTargetFlag = targetFlag;
|
|
1845
|
-
this.
|
|
1919
|
+
this._currentIterLoopLabels = loopLabels;
|
|
1846
1920
|
this._iterLoopDepth = 0;
|
|
1921
|
+
// Loops of an enclosing callback are not JS labels in this one.
|
|
1922
|
+
this._innerLoopLabels = new Set();
|
|
1923
|
+
this._jsLabels = [];
|
|
1847
1924
|
|
|
1848
1925
|
if (assignPreamble) assignPreamble();
|
|
1849
1926
|
this.genBlock(body);
|
|
@@ -1853,14 +1930,16 @@ export const statementGenMethods = {
|
|
|
1853
1930
|
this._iterReturnFlag = prev.ret;
|
|
1854
1931
|
this._iterReturnVar = prev.retVar;
|
|
1855
1932
|
this._iterTargetFlag = prev.target;
|
|
1856
|
-
this.
|
|
1933
|
+
this._currentIterLoopLabels = prev.loopLabels;
|
|
1857
1934
|
this._iterLoopDepth = prev.loopDepth;
|
|
1935
|
+
this._innerLoopLabels = prev.innerLabels;
|
|
1936
|
+
this._jsLabels = prev.jsLabels;
|
|
1858
1937
|
|
|
1859
1938
|
this.line("return true;");
|
|
1860
1939
|
});
|
|
1861
1940
|
this.line("});");
|
|
1862
1941
|
this.line(`if (${retFlag}) return ${retVar};`);
|
|
1863
|
-
this._emitIterTargetDispatch(targetFlag);
|
|
1942
|
+
this._emitIterTargetDispatch(targetFlag, loopLabels);
|
|
1864
1943
|
});
|
|
1865
1944
|
this.line("}");
|
|
1866
1945
|
this._iterDepth--;
|
|
@@ -1869,7 +1948,8 @@ export const statementGenMethods = {
|
|
|
1869
1948
|
genIteratorFor(stmt) {
|
|
1870
1949
|
const range = stmt.init.rhs[0];
|
|
1871
1950
|
const isAssign = stmt.init.kind === "AssignStmt";
|
|
1872
|
-
|
|
1951
|
+
// A non-identifier target is stored through _emitRangeVar, not by name.
|
|
1952
|
+
const lhs = stmt.init.lhs.map((e) => e.name ?? "_$target");
|
|
1873
1953
|
const yieldTypes = range._yieldParams ?? [];
|
|
1874
1954
|
if (isAssign) {
|
|
1875
1955
|
const cbParams = lhs.map((_, i) => `__p${i}`);
|
|
@@ -1881,12 +1961,9 @@ export const statementGenMethods = {
|
|
|
1881
1961
|
const isStruct =
|
|
1882
1962
|
this._isStructType(yType) ||
|
|
1883
1963
|
this._isStructType(stmt.init.lhs[i]?._type);
|
|
1884
|
-
if (isStruct)
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
} else {
|
|
1888
|
-
this.line(`${lhs[i]} = ${cbParams[i]};`);
|
|
1889
|
-
}
|
|
1964
|
+
if (isStruct) this.useHelper("sclone");
|
|
1965
|
+
const value = isStruct ? `__sclone(${cbParams[i]})` : cbParams[i];
|
|
1966
|
+
this._emitRangeVar(stmt.init.lhs[i], value, true);
|
|
1890
1967
|
}
|
|
1891
1968
|
}
|
|
1892
1969
|
};
|
|
@@ -1895,7 +1972,7 @@ export const statementGenMethods = {
|
|
|
1895
1972
|
params,
|
|
1896
1973
|
stmt.body,
|
|
1897
1974
|
assignPreamble,
|
|
1898
|
-
stmt
|
|
1975
|
+
this._loopLabels(stmt),
|
|
1899
1976
|
);
|
|
1900
1977
|
return;
|
|
1901
1978
|
}
|
|
@@ -1920,7 +1997,7 @@ export const statementGenMethods = {
|
|
|
1920
1997
|
params,
|
|
1921
1998
|
stmt.body,
|
|
1922
1999
|
clonePreamble,
|
|
1923
|
-
stmt
|
|
2000
|
+
this._loopLabels(stmt),
|
|
1924
2001
|
);
|
|
1925
2002
|
},
|
|
1926
2003
|
|
|
@@ -1931,7 +2008,7 @@ export const statementGenMethods = {
|
|
|
1931
2008
|
"",
|
|
1932
2009
|
stmt.body,
|
|
1933
2010
|
null,
|
|
1934
|
-
stmt
|
|
2011
|
+
this._loopLabels(stmt),
|
|
1935
2012
|
);
|
|
1936
2013
|
},
|
|
1937
2014
|
|
|
@@ -2032,14 +2109,8 @@ export const statementGenMethods = {
|
|
|
2032
2109
|
if (stmt.lhs.length === 1) {
|
|
2033
2110
|
if (stmt.op !== "=" && is64BitInt(stmt.lhs[0]._type)) {
|
|
2034
2111
|
const binOp = stmt.op.slice(0, -1);
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
stmt.rhs[0]?._type?.kind === "untyped" &&
|
|
2038
|
-
!r.endsWith("n") &&
|
|
2039
|
-
/^-?\d+$/.test(r)
|
|
2040
|
-
) {
|
|
2041
|
-
r = `${r}n`;
|
|
2042
|
-
}
|
|
2112
|
+
// Same operand coercion as the statement path (`_genAssignStmt`).
|
|
2113
|
+
const r = this._untypedTo64(rhs[0], stmt.rhs[0]);
|
|
2043
2114
|
const l = this.genExpr(stmt.lhs[0]);
|
|
2044
2115
|
return `${l} = ${this._gen64BitBinary(binOp, l, r, stmt.lhs[0]._type)}`;
|
|
2045
2116
|
}
|
|
@@ -2059,6 +2130,11 @@ export const statementGenMethods = {
|
|
|
2059
2130
|
return `[${lhs.join(", ")}] = ${r}`;
|
|
2060
2131
|
},
|
|
2061
2132
|
|
|
2133
|
+
_isMapTag(tag) {
|
|
2134
|
+
const t = tag._type?.kind === "named" ? tag._type.underlying : tag._type;
|
|
2135
|
+
return t?.kind === "map" || tag._coreType?.kind === "map";
|
|
2136
|
+
},
|
|
2137
|
+
|
|
2062
2138
|
// Interface-typed tags may hold boxed named values; cases compare with __ifeq.
|
|
2063
2139
|
_switchTagAndCases(stmt) {
|
|
2064
2140
|
const tag = stmt.tag ? this.genExpr(stmt.tag) : "true";
|
|
@@ -2073,6 +2149,37 @@ export const statementGenMethods = {
|
|
|
2073
2149
|
caseExpr: (e) => `__equal(${tmp}, ${this.genExpr(e)})`,
|
|
2074
2150
|
};
|
|
2075
2151
|
}
|
|
2152
|
+
if (stmt.tag && isComplex(stmt.tag._type)) {
|
|
2153
|
+
// A complex tag is an object: cases compare its parts.
|
|
2154
|
+
this._swCounter = (this._swCounter ?? 0) + 1;
|
|
2155
|
+
const tmp = `__sw${this._swCounter}`;
|
|
2156
|
+
this.line(`const ${tmp} = ${tag};`);
|
|
2157
|
+
return {
|
|
2158
|
+
tag: "true",
|
|
2159
|
+
caseExpr: (e) =>
|
|
2160
|
+
this._genComplexEquality(
|
|
2161
|
+
"===",
|
|
2162
|
+
"&&",
|
|
2163
|
+
tmp,
|
|
2164
|
+
this._genComplexOperand(e),
|
|
2165
|
+
),
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
if (stmt.tag && this._isMapTag(stmt.tag)) {
|
|
2169
|
+
// A nil map is a sentinel object in JS, not `null`: `case nil`
|
|
2170
|
+
// goes through `__isnm`, as `m == nil` does.
|
|
2171
|
+
this.useHelper(this.strict ? "nilMap" : "nm");
|
|
2172
|
+
this._swCounter = (this._swCounter ?? 0) + 1;
|
|
2173
|
+
const tmp = `__sw${this._swCounter}`;
|
|
2174
|
+
this.line(`const ${tmp} = ${tag};`);
|
|
2175
|
+
return {
|
|
2176
|
+
tag: "true",
|
|
2177
|
+
caseExpr: (e) =>
|
|
2178
|
+
this._isNilLiteral(e)
|
|
2179
|
+
? `__isnm(${tmp})`
|
|
2180
|
+
: `${tmp} === ${this.genExpr(e)}`,
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2076
2183
|
if (!stmt.tag || !this._isIfaceType(stmt.tag._type))
|
|
2077
2184
|
return { tag, caseExpr: (e) => this.genExpr(e) };
|
|
2078
2185
|
this.useHelper("ifeq");
|
|
@@ -2125,7 +2232,9 @@ export const statementGenMethods = {
|
|
|
2125
2232
|
} else if (
|
|
2126
2233
|
stmt.tag &&
|
|
2127
2234
|
(this._isStructOrArrayType(stmt.tag._type) ||
|
|
2128
|
-
this._isIfaceType(stmt.tag._type)
|
|
2235
|
+
this._isIfaceType(stmt.tag._type) ||
|
|
2236
|
+
isComplex(stmt.tag._type) ||
|
|
2237
|
+
this._isMapTag(stmt.tag))
|
|
2129
2238
|
) {
|
|
2130
2239
|
// The tag temporary is a `const`, which cannot directly follow a label.
|
|
2131
2240
|
this.line("{");
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
typedArrayConstructorForElem,
|
|
14
14
|
typeStr,
|
|
15
15
|
} from "../../typechecker/types.js";
|
|
16
|
-
import {
|
|
16
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
17
|
+
import { isGenericComposite, mentionsTypeParam } from "./expressions.js";
|
|
17
18
|
|
|
18
19
|
// True when type node `node` names one of the type parameters `names`.
|
|
19
20
|
function typeNodeMentions(node, names) {
|
|
@@ -87,6 +88,8 @@ export const typeInfoGenMethods = {
|
|
|
87
88
|
|
|
88
89
|
_nodeUsesTypeInfo(node, names) {
|
|
89
90
|
if (node._ifaceKeyDyn && names.has(node._ifaceKeyParam)) return true;
|
|
91
|
+
// A map or slice entering an interface is tagged by its instantiated type.
|
|
92
|
+
if (isGenericComposite(node._ifaceSrc)) return true;
|
|
90
93
|
if (node._ifaceSrc?.kind === "typeParam" && names.has(node._ifaceSrc.name))
|
|
91
94
|
return true;
|
|
92
95
|
switch (node.kind) {
|
|
@@ -180,20 +183,33 @@ export const typeInfoGenMethods = {
|
|
|
180
183
|
const unbox = this._boxesInInterface(t) ? "true" : "false";
|
|
181
184
|
const box = this._ifaceKeyBox(t) ?? this._ptrMethodBox(t);
|
|
182
185
|
const fmtBox = this._fmtBoxFor(t);
|
|
183
|
-
const tail = fmtBox
|
|
184
|
-
? `, ${box ?? "null"}, ${fmtBox}`
|
|
185
|
-
: box
|
|
186
|
-
? `, ${box}`
|
|
187
|
-
: "";
|
|
186
|
+
const tail = this._typeInfoTail(t, box, fmtBox);
|
|
188
187
|
return `__ti(${base}, ${JSON.stringify(k)}, ${JSON.stringify(typeStr(t))}, ${is}, ${unbox}${tail})`;
|
|
189
188
|
},
|
|
190
189
|
|
|
190
|
+
// The optional trailing arguments of `__ti` (`b`, `fb`, `nm`). `typeStr`
|
|
191
|
+
// drops the `...` of a variadic func type, so a func type also carries its
|
|
192
|
+
// spelled name (see `__tnm`).
|
|
193
|
+
_typeInfoTail(t, box, fmtBox) {
|
|
194
|
+
if (t.kind === "func")
|
|
195
|
+
return `, ${box ?? "null"}, ${fmtBox ?? "null"}, ${JSON.stringify(this._dynTypeName(t))}`;
|
|
196
|
+
if (fmtBox) return `, ${box ?? "null"}, ${fmtBox}`;
|
|
197
|
+
return box ? `, ${box}` : "";
|
|
198
|
+
},
|
|
199
|
+
|
|
191
200
|
// How `__ik` tells the dynamic type of an interface map key apart where
|
|
192
201
|
// the JS value cannot: the numeric type name for types sharing a JS
|
|
193
202
|
// representation with `int`/`float64`/`int64`, `*` for pointers (keyed by
|
|
194
203
|
// identity) and `!type` for types Go cannot hash.
|
|
195
204
|
_ifaceKeyTag(t) {
|
|
196
|
-
if (!t ||
|
|
205
|
+
if (!t || mentionsTypeParam(t)) return null;
|
|
206
|
+
if (t.kind === "named") {
|
|
207
|
+
// A defined slice, map or func type: a literal of it carries no tag.
|
|
208
|
+
const u = this._underlyingOf(t)?.kind;
|
|
209
|
+
return u === "slice" || u === "map" || u === "func"
|
|
210
|
+
? `!${goTypeName(t)}`
|
|
211
|
+
: null;
|
|
212
|
+
}
|
|
197
213
|
if (t.kind === "pointer") return "*";
|
|
198
214
|
if (t.kind === "slice" || t.kind === "map" || t.kind === "func")
|
|
199
215
|
return `!${this._dynTypeName(t)}`;
|
|
@@ -227,7 +243,7 @@ export const typeInfoGenMethods = {
|
|
|
227
243
|
const tag = this._dynTypeTag(t);
|
|
228
244
|
if (!tag) return base;
|
|
229
245
|
this.useHelper("ifaceBox");
|
|
230
|
-
const ptr = t
|
|
246
|
+
const ptr = this._dynTagFlags(t);
|
|
231
247
|
return `(__v) => __ity(${base ? `(${base})(__v)` : "__v"}, ${JSON.stringify(tag)}${ptr})`;
|
|
232
248
|
},
|
|
233
249
|
|
|
@@ -239,6 +255,12 @@ export const typeInfoGenMethods = {
|
|
|
239
255
|
this.useHelper("ifaceBox");
|
|
240
256
|
return "(__v) => __ifp(__v)";
|
|
241
257
|
}
|
|
258
|
+
// A struct value is marked as a value, so a pointer boxed earlier by the
|
|
259
|
+
// same generic code does not make it read as `*T`.
|
|
260
|
+
if (u?.kind === "struct" && t.kind !== "pointer") {
|
|
261
|
+
this.useHelper("ifaceBox");
|
|
262
|
+
return "(__v) => __ifv(__v)";
|
|
263
|
+
}
|
|
242
264
|
if (u?.kind === "map") {
|
|
243
265
|
this.useHelper("ifaceBox");
|
|
244
266
|
return "(__v) => __ifm(__v)";
|
|
@@ -247,6 +269,62 @@ export const typeInfoGenMethods = {
|
|
|
247
269
|
return null;
|
|
248
270
|
},
|
|
249
271
|
|
|
272
|
+
// Extra `__ity` arguments: `, true` marks a pointer and `, false, true` a
|
|
273
|
+
// defined slice type, whose nil value keeps its type as a tagged sentinel.
|
|
274
|
+
_dynTagFlags(t) {
|
|
275
|
+
if (t?.kind === "pointer") return ", true";
|
|
276
|
+
if (this._isNamedComposite(t) && this._underlyingOf(t)?.kind === "slice")
|
|
277
|
+
return ", false, true";
|
|
278
|
+
return "";
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// JS expression for the run-time type tag of a map, slice or array type `t`
|
|
282
|
+
// that mentions type parameters, built from their type information (see
|
|
283
|
+
// `__ti`); null when some part cannot be spelled.
|
|
284
|
+
_genericMapTag(t) {
|
|
285
|
+
const part = (x) => {
|
|
286
|
+
if (!mentionsTypeParam(x)) return JSON.stringify(this._dynTypeName(x));
|
|
287
|
+
switch (x.kind) {
|
|
288
|
+
case "typeParam": {
|
|
289
|
+
const f = this._typeParamZeroFactory(x.name);
|
|
290
|
+
return f ? `__tnm(${f})` : null;
|
|
291
|
+
}
|
|
292
|
+
case "slice": {
|
|
293
|
+
const e = part(x.elem);
|
|
294
|
+
return e && `"[]" + ${e}`;
|
|
295
|
+
}
|
|
296
|
+
case "array": {
|
|
297
|
+
const e = part(x.elem);
|
|
298
|
+
return e && `"[${x.size}]" + ${e}`;
|
|
299
|
+
}
|
|
300
|
+
case "pointer": {
|
|
301
|
+
const e = part(x.base);
|
|
302
|
+
return e && `"*" + ${e}`;
|
|
303
|
+
}
|
|
304
|
+
case "map": {
|
|
305
|
+
const k = part(x.key);
|
|
306
|
+
const v = part(x.value);
|
|
307
|
+
return k && v && `"map[" + ${k} + "]" + ${v}`;
|
|
308
|
+
}
|
|
309
|
+
default:
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
return part(t);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// A defined func type whose values carry their name as a run-time tag
|
|
317
|
+
// (one with methods is boxed as a class instead).
|
|
318
|
+
_isTaggedFuncType(t) {
|
|
319
|
+
return (
|
|
320
|
+
t?.kind === "named" &&
|
|
321
|
+
!mentionsTypeParam(t) &&
|
|
322
|
+
!t.typeArgs?.length &&
|
|
323
|
+
this._underlyingOf(t)?.kind === "func" &&
|
|
324
|
+
!this._namedBoxInfo(t)
|
|
325
|
+
);
|
|
326
|
+
},
|
|
327
|
+
|
|
250
328
|
// The run-time type tag of an unnamed slice, map or non-struct pointer
|
|
251
329
|
// entering an interface, or null when the value needs none. The tag lets
|
|
252
330
|
// assertions and type switches tell `[]int32` from `[]uint8`, `*int` from
|
|
@@ -272,6 +350,27 @@ export const typeInfoGenMethods = {
|
|
|
272
350
|
return null;
|
|
273
351
|
},
|
|
274
352
|
|
|
353
|
+
// Whether Go cannot hash a value of type `t` (it contains a slice, map or
|
|
354
|
+
// func), so using it as an interface map key panics.
|
|
355
|
+
_isUnhashableType(t, depth = 0) {
|
|
356
|
+
if (!t || depth > 8 || mentionsTypeParam(t)) return false;
|
|
357
|
+
const u = this._underlyingOf(t) ?? t;
|
|
358
|
+
switch (u.kind) {
|
|
359
|
+
case "slice":
|
|
360
|
+
case "map":
|
|
361
|
+
case "func":
|
|
362
|
+
return true;
|
|
363
|
+
case "array":
|
|
364
|
+
return this._isUnhashableType(u.elem, depth + 1);
|
|
365
|
+
case "struct":
|
|
366
|
+
return [...(u.fields?.values() ?? [])].some((f) =>
|
|
367
|
+
this._isUnhashableType(f, depth + 1),
|
|
368
|
+
);
|
|
369
|
+
default:
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
|
|
275
374
|
// A named slice, array or map type: its values are plain JS arrays and
|
|
276
375
|
// objects, told apart in an interface by a tag carrying the type's name.
|
|
277
376
|
_isNamedComposite(t) {
|
|
@@ -302,6 +401,22 @@ export const typeInfoGenMethods = {
|
|
|
302
401
|
return `map[${s(t.key)}]${s(t.value)}`;
|
|
303
402
|
case "pointer":
|
|
304
403
|
return `*${s(t.base)}`;
|
|
404
|
+
case "func": {
|
|
405
|
+
// Go's spelling: no result is `func()`, not `func() void`.
|
|
406
|
+
const rs = (t.returns ?? []).filter(
|
|
407
|
+
(r) => !(r?.kind === "basic" && r.name === "void"),
|
|
408
|
+
);
|
|
409
|
+
const ps = (t.params ?? []).map((p, i) =>
|
|
410
|
+
t.variadic && i === t.params.length - 1 ? `...${s(p)}` : s(p),
|
|
411
|
+
);
|
|
412
|
+
const res =
|
|
413
|
+
rs.length === 0
|
|
414
|
+
? ""
|
|
415
|
+
: rs.length === 1
|
|
416
|
+
? ` ${s(rs[0])}`
|
|
417
|
+
: ` (${rs.map(s).join(", ")})`;
|
|
418
|
+
return `func(${ps.join(", ")})${res}`;
|
|
419
|
+
}
|
|
305
420
|
case "struct":
|
|
306
421
|
if (t.name || !t._structFields) return typeStr(t);
|
|
307
422
|
return `struct{${t._structFields
|