@scriptc/compiler 0.0.7 → 0.0.8
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/dist/backend/emission/emit-exprs.js +47 -1
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-island.js +9 -1
- package/dist/backend/emission/emit-island.js.map +1 -1
- package/dist/backend/emission/emit-stmts.js +3 -1
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +22 -8
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.js +5 -1
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +4 -3
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/llvm/emitter.js +94 -7
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +43 -1
- package/dist/diagnostics/diagnostic.js +103 -5
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lib-contract.d.ts +24 -6
- package/dist/frontend/lib-contract.js +125 -44
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.js +16 -1
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +47 -7
- package/dist/frontend/lowering/lower-calls.js +560 -36
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +14 -1
- package/dist/frontend/lowering/lower-classes.js +85 -15
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +43 -1
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.js +1 -1
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +26 -3
- package/dist/frontend/lowering/lower-exprs.js +455 -40
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +7 -0
- package/dist/frontend/lowering/lower-island.js +71 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.d.ts +1 -1
- package/dist/frontend/lowering/lower-mixins.js +1 -1
- package/dist/frontend/lowering/lower-modules.js +95 -4
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +421 -43
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +19 -3
- package/dist/frontend/lowering/lowerer.js +188 -11
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/types.d.ts +28 -1
- package/dist/frontend/types.js +300 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +37 -7
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +28 -2
- package/dist/ir/nodes.js +35 -3
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +35 -6
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/profile.d.ts +17 -3
- package/dist/library/profile.js +38 -9
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar.js +162 -43
- package/dist/library/sidecar.js.map +1 -1
- package/dist/library/trap-teaching.d.ts +11 -0
- package/dist/library/trap-teaching.js +49 -0
- package/dist/library/trap-teaching.js.map +1 -0
- package/package.json +2 -2
- package/surface-manifest.json +37 -2
|
@@ -13,8 +13,9 @@ import { isProvenanceSourceFile } from "../provenance-registry.js";
|
|
|
13
13
|
import { lowerImportEquals, nsWritableTarget } from "./lower-namespaces.js";
|
|
14
14
|
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
|
15
15
|
import { lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape } from "./lower-containers.js";
|
|
16
|
-
import { bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
16
|
+
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
17
17
|
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
18
|
+
import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
|
|
18
19
|
import { lowerStreamUnderscoreAssign, streamClassAliasDecl, streamSidesOf } from "./lower-stream.js";
|
|
19
20
|
import { lowerHttpResPropertyAssignment, lowerServerCloseOverrideAssignment } from "./lower-server.js";
|
|
20
21
|
import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf } from "./lower-builtins.js";
|
|
@@ -1295,7 +1296,25 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1295
1296
|
continue;
|
|
1296
1297
|
const loc = locOf(el);
|
|
1297
1298
|
if (el.dotDotDotToken) {
|
|
1298
|
-
|
|
1299
|
+
// `{ a, ...rest }` over a class instance: the remaining
|
|
1300
|
+
// instance FIELDS pack fresh (classInstanceRestValue — JS's
|
|
1301
|
+
// CopyDataProperties copies own enumerable properties, which
|
|
1302
|
+
// for a class instance are exactly the fields).
|
|
1303
|
+
const consumed = new Set();
|
|
1304
|
+
for (const sib of pattern.elements) {
|
|
1305
|
+
if (sib === el || sib.name === undefined || sib.dotDotDotToken)
|
|
1306
|
+
continue;
|
|
1307
|
+
const keyNode = sib.propertyName ?? (ts.isIdentifier(sib.name) ? sib.name : null);
|
|
1308
|
+
if (keyNode === null)
|
|
1309
|
+
continue; // defensive: a pattern target always carries a propertyName
|
|
1310
|
+
const folded = patternKeyNameOf(L, keyNode);
|
|
1311
|
+
if (folded === null) {
|
|
1312
|
+
L.unsupported("SC1031", el, "rest bindings beside computed keys (the consumed set is a runtime fact)");
|
|
1313
|
+
}
|
|
1314
|
+
consumed.add(folded);
|
|
1315
|
+
}
|
|
1316
|
+
L.bindPatternTarget(el.name, classInstanceRestValue(L, el, srcRef, srcType, info, consumed, patternBindingType(L, el.name)), isLet, out);
|
|
1317
|
+
continue;
|
|
1299
1318
|
}
|
|
1300
1319
|
const prop = el.propertyName ?? el.name;
|
|
1301
1320
|
const propName = ts.isIdentifier(prop) || ts.isComputedPropertyName(prop) || ts.isStringLiteralLike(prop) || ts.isNumericLiteral(prop)
|
|
@@ -1319,6 +1338,14 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1319
1338
|
field: propName,
|
|
1320
1339
|
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
1321
1340
|
}, loc, el);
|
|
1341
|
+
// A defaulted GETTER result lands in a hidden temp first: the
|
|
1342
|
+
// default's ternary mentions its operand twice (test + present
|
|
1343
|
+
// arm), and JS calls the getter once per element.
|
|
1344
|
+
if (el.initializer) {
|
|
1345
|
+
const got = L.declareHiddenLocal("%dget", value.type);
|
|
1346
|
+
out.push({ kind: "varDecl", localId: got.id, init: value, loc });
|
|
1347
|
+
value = { kind: "varRef", localId: got.id, type: value.type, loc };
|
|
1348
|
+
}
|
|
1322
1349
|
}
|
|
1323
1350
|
else {
|
|
1324
1351
|
L.unsupported("SC1031", el, L.findMethodOn(info, propName)
|
|
@@ -1372,12 +1399,20 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out) {
|
|
|
1372
1399
|
if (getSlotT?.kind !== "func") {
|
|
1373
1400
|
L.unsupported("SC1031", el, `destructuring the setter-only property '${propName}' (Node would bind undefined)`);
|
|
1374
1401
|
}
|
|
1375
|
-
if (el.initializer) {
|
|
1376
|
-
L.unsupported("SC1031", el, "defaults on destructured get/set accessor properties");
|
|
1377
|
-
}
|
|
1378
1402
|
const loc = locOf(el);
|
|
1379
1403
|
const closure = { kind: "recordGet", obj: srcRef(), shapeId: srcType.shapeId, field: `%get:${propName}`, type: getSlotT, loc };
|
|
1380
|
-
|
|
1404
|
+
// The getter call IS the element's read; a default applies to its
|
|
1405
|
+
// RESULT exactly like a data field's (undefined-arm test, lazy).
|
|
1406
|
+
// The result lands in a hidden temp first: the default's ternary
|
|
1407
|
+
// mentions its operand twice (test + present arm), and the getter
|
|
1408
|
+
// must run ONCE (JS calls it once per element).
|
|
1409
|
+
let accessorValue = { kind: "callValue", callee: closure, args: [], type: getSlotT.ret, loc };
|
|
1410
|
+
if (el.initializer) {
|
|
1411
|
+
const got = L.declareHiddenLocal("%dget", accessorValue.type);
|
|
1412
|
+
out.push({ kind: "varDecl", localId: got.id, init: accessorValue, loc });
|
|
1413
|
+
accessorValue = applyBindingDefault(L, el, { kind: "varRef", localId: got.id, type: accessorValue.type, loc });
|
|
1414
|
+
}
|
|
1415
|
+
L.bindPatternTarget(el.name, accessorValue, isLet, out);
|
|
1381
1416
|
continue;
|
|
1382
1417
|
}
|
|
1383
1418
|
const fieldType = shape.fields.find((f) => f.name === propName)?.type;
|
|
@@ -1440,7 +1475,12 @@ export function patternKeyNameOf(L, prop) {
|
|
|
1440
1475
|
return prop.text;
|
|
1441
1476
|
if (ts.isComputedPropertyName(prop))
|
|
1442
1477
|
return L.foldedStringKeyOf(prop.expression);
|
|
1443
|
-
|
|
1478
|
+
// A numeric-literal key spells JS's canonical ToPropertyKey form
|
|
1479
|
+
// directly (`{ 2: x }` names the field "2") — a pattern position is
|
|
1480
|
+
// not an expression, so the checker-type fold below does not apply.
|
|
1481
|
+
if (ts.isNumericLiteral(prop))
|
|
1482
|
+
return String(Number(prop.text));
|
|
1483
|
+
if (ts.isStringLiteral(prop) || ts.isNoSubstitutionTemplateLiteral(prop)) {
|
|
1444
1484
|
return L.foldedStringKeyOf(prop);
|
|
1445
1485
|
}
|
|
1446
1486
|
return null;
|
|
@@ -1681,6 +1721,63 @@ function objectRestValue(L, el, srcRef, srcType, shape) {
|
|
|
1681
1721
|
});
|
|
1682
1722
|
return { kind: "recordLit", fields, type: restT, loc };
|
|
1683
1723
|
}
|
|
1724
|
+
/** The fresh record for `{ a, ...rest }` over a CLASS-INSTANCE source:
|
|
1725
|
+
* JS's CopyDataProperties copies the OWN ENUMERABLE properties — the
|
|
1726
|
+
* instance FIELDS, base chain included, in property-creation order
|
|
1727
|
+
* (base constructors assign theirs first) — and never the prototype
|
|
1728
|
+
* members (methods and accessors stay behind; the checker's rest type
|
|
1729
|
+
* excludes them too, so the two sides agree). The packed record must
|
|
1730
|
+
* agree with the checker's rest type field-for-field — a non-public
|
|
1731
|
+
* field IS copied by JS but the rest type cannot name it (silent
|
|
1732
|
+
* divergence; fence) — and key-for-key in ORDER: the packed shape's
|
|
1733
|
+
* JSON/Object.keys order is first-seen global metadata, and a checker
|
|
1734
|
+
* rest type that reorders (own-before-inherited) would make the
|
|
1735
|
+
* enumeration surfaces diverge from Node, so it fences instead of
|
|
1736
|
+
* silently reordering. Runtime-provided chains (Error/EventEmitter/
|
|
1737
|
+
* stream roots) carry runtime-internal state no record copy can
|
|
1738
|
+
* reproduce — fenced. ES-#private fields are own but non-enumerable:
|
|
1739
|
+
* JS skips them and so does the packing. */
|
|
1740
|
+
function classInstanceRestValue(L, blame, srcRef, srcType, info, consumed, restT) {
|
|
1741
|
+
const loc = locOf(blame);
|
|
1742
|
+
for (let c = info; c; c = c.base) {
|
|
1743
|
+
if (c.builtinError || c.builtinEmitter || c.builtinStream) {
|
|
1744
|
+
L.unsupported("SC1031", blame, "rest bindings over runtime-provided class instances (Error/EventEmitter/stream internals have no record form)");
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
if (restT?.kind !== "record") {
|
|
1748
|
+
L.unsupported("SC1031", blame, restT
|
|
1749
|
+
? `rest bindings packing the remaining properties as '${L.fmt(restT)}' (only plain record types pack)`
|
|
1750
|
+
: "rest bindings whose packed type has no static mapping (only plain record types pack)");
|
|
1751
|
+
}
|
|
1752
|
+
const restShape = L.shapes.get(restT.shapeId);
|
|
1753
|
+
if (!restShape)
|
|
1754
|
+
throw new Error(`lowerer bug: rest binding over unknown shape ${restT.shapeId}`);
|
|
1755
|
+
if (restShape.indexValue || restShape.tuple) {
|
|
1756
|
+
L.unsupported("SC1031", blame, "rest bindings over index-signature shapes (the undeclared entries would need overflow packing)");
|
|
1757
|
+
}
|
|
1758
|
+
const remaining = info.def.fields.filter((f) => !f.name.startsWith("#") && !f.name.startsWith("%") && !consumed.has(f.name));
|
|
1759
|
+
for (const f of remaining) {
|
|
1760
|
+
if (!restShape.fields.some((rf) => rf.name === f.name)) {
|
|
1761
|
+
L.unsupported("SC1031", blame, `rest bindings over instances of '${info.def.jsName ?? info.def.name}' (JS copies the non-public field '${f.name}' into the rest object, which the rest type cannot name)`);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
for (const rf of restShape.fields) {
|
|
1765
|
+
if (!remaining.some((f) => f.name === rf.name)) {
|
|
1766
|
+
L.unsupported("SC1031", blame, `the rest field '${rf.name}' is not an instance field of '${info.def.jsName ?? info.def.name}'`);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
const emitOrder = restShape.declaredOrder ?? restShape.fields.map((f) => f.name);
|
|
1770
|
+
if (emitOrder.length !== remaining.length || emitOrder.some((n, i) => n !== remaining[i].name)) {
|
|
1771
|
+
L.unsupported("SC1031", blame, "rest bindings over class instances whose packed key order cannot match Node's (Object.keys/JSON.stringify would enumerate the copied fields in a different order)");
|
|
1772
|
+
}
|
|
1773
|
+
const fields = remaining.map((f) => {
|
|
1774
|
+
const fieldType = info.fields.get(f.name) ?? f.type;
|
|
1775
|
+
const read = L.fieldGetExpr({ container: "class", obj: srcRef(), className: srcType.className, field: f.name, fieldType }, loc, blame);
|
|
1776
|
+
const restFT = restShape.fields.find((rf) => rf.name === f.name).type;
|
|
1777
|
+
return { name: f.name, value: L.coerceInto(blame, read, restFT) };
|
|
1778
|
+
});
|
|
1779
|
+
return { kind: "recordLit", fields, type: restT, loc };
|
|
1780
|
+
}
|
|
1684
1781
|
/** The fences the island's element-wise object path shares: no rest
|
|
1685
1782
|
* (surplus packing over a handle) and no defaults (an engine undefined
|
|
1686
1783
|
* test) — the static positions now take both through their own
|
|
@@ -2144,12 +2241,19 @@ export function lowerVarDecl(L, decl, isLet) {
|
|
|
2144
2241
|
// shapes (reassigned bindings, declarations inside functions) fence
|
|
2145
2242
|
// by name inside; a collection-time report dedupes.
|
|
2146
2243
|
{
|
|
2147
|
-
const gfnNode = bindingGenericFnNodeOf(decl);
|
|
2244
|
+
const gfnNode = bindingGenericFnNodeOf(decl) ?? bindingContextualGenericFnNodeOf(L, decl);
|
|
2148
2245
|
if (gfnNode) {
|
|
2149
2246
|
bindingGenericFnInfoOf(L, decl, gfnNode);
|
|
2150
2247
|
return null;
|
|
2151
2248
|
}
|
|
2152
2249
|
}
|
|
2250
|
+
// `const h = id` — an ALIAS of a generic function: the alias's symbol
|
|
2251
|
+
// registers the target's info (calls and pinned values resolve like
|
|
2252
|
+
// the target's own name), the binding has no runtime value, and the
|
|
2253
|
+
// statement emits nothing (collectGlobals skipped module-scope
|
|
2254
|
+
// globals by the same test; block-scoped aliases register here).
|
|
2255
|
+
if (bindingGenericFnAliasInfoOf(L, decl))
|
|
2256
|
+
return null;
|
|
2153
2257
|
// `const knownBy = (cmd) => ...` — an IMPLICIT-ANY function-value
|
|
2154
2258
|
// binding in npm-static JS (function-scope bindings register here, in
|
|
2155
2259
|
// source order before any use can lower; file-scope ones registered at
|
|
@@ -2403,6 +2507,20 @@ export function lowerVarDecl(L, decl, isLet) {
|
|
|
2403
2507
|
// bridge is an independent observer of the same settlement).
|
|
2404
2508
|
if (init.type.kind === "jsval" && type.kind === "promise")
|
|
2405
2509
|
type = JSVAL;
|
|
2510
|
+
// A STATIC array of island HANDLES under an unannotated declared type
|
|
2511
|
+
// spelling evolved elements (`const kept = fns.filter(...)` over an
|
|
2512
|
+
// evolving-`any` receiver — the handle-element lowering keeps the
|
|
2513
|
+
// elements jsval, while tsc's evolving-array analysis types the
|
|
2514
|
+
// result by the pushed elements): the value's element type is the
|
|
2515
|
+
// truth, so the binding keeps the handle-element array (the
|
|
2516
|
+
// island-HANDLE local story, one level down) and later method calls
|
|
2517
|
+
// ride the handle-element lowering. Explicit annotations keep the
|
|
2518
|
+
// validated-boundary fence.
|
|
2519
|
+
if (!decl.type &&
|
|
2520
|
+
init.type.kind === "array" && init.type.elem.kind === "jsval" &&
|
|
2521
|
+
type.kind === "array" && type.elem.kind !== "jsval") {
|
|
2522
|
+
type = init.type;
|
|
2523
|
+
}
|
|
2406
2524
|
// `const runner = options.runner || defaultRunner`: the checker types
|
|
2407
2525
|
// the || as the union/merge of two structurally-compatible function
|
|
2408
2526
|
// types — a signature whose RETURN is the arms' union, or the
|
|
@@ -2434,6 +2552,14 @@ export function lowerVarDecl(L, decl, isLet) {
|
|
|
2434
2552
|
type = init.type;
|
|
2435
2553
|
}
|
|
2436
2554
|
}
|
|
2555
|
+
// `const r: Repo = new MemRepo()` over an all-generic-method
|
|
2556
|
+
// interface: the binding keeps the initializer's CLASS representation
|
|
2557
|
+
// (the record shape maps empty and the width copy would drop the
|
|
2558
|
+
// class the generic-method calls monomorphize against) — see
|
|
2559
|
+
// genericIfaceBindingKeepsClass.
|
|
2560
|
+
if (type.kind === "record" && init.type.kind === "object" && genericIfaceBindingKeepsClass(L, decl, type)) {
|
|
2561
|
+
type = init.type;
|
|
2562
|
+
}
|
|
2437
2563
|
// A TDZ box minted DURING this very initializer (a callback inside it
|
|
2438
2564
|
// captured this const — predeclareForwardCapture's current-statement
|
|
2439
2565
|
// case): the binding and its scope-entry varDecl already exist, so
|
|
@@ -3891,6 +4017,129 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
3891
4017
|
}
|
|
3892
4018
|
return { stmts: out, value };
|
|
3893
4019
|
}
|
|
4020
|
+
// A CLASS-INSTANCE source (`({ a, b } = inst)`): the declaration
|
|
4021
|
+
// path's desugar in assignment position — one member read per
|
|
4022
|
+
// element, left to right (declared fields read their slots, accessor
|
|
4023
|
+
// properties call their getters through the same fieldGetExpr
|
|
4024
|
+
// dispatch), defaults against the TARGET's own type, rest packing
|
|
4025
|
+
// the remaining instance fields (classInstanceRestValue). Methods
|
|
4026
|
+
// and names no class on the chain declares keep the declaration
|
|
4027
|
+
// path's named fences.
|
|
4028
|
+
if (init.type.kind === "object") {
|
|
4029
|
+
const objT = init.type;
|
|
4030
|
+
if (!L.classes.get(objT.className))
|
|
4031
|
+
L.flushDeferredClass(objT.className);
|
|
4032
|
+
const info = L.classes.get(objT.className);
|
|
4033
|
+
if (info) {
|
|
4034
|
+
for (const prop of target.properties) {
|
|
4035
|
+
const propLoc = locOf(prop);
|
|
4036
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
4037
|
+
let restTo = prop.expression;
|
|
4038
|
+
while (ts.isParenthesizedExpression(restTo))
|
|
4039
|
+
restTo = restTo.expression;
|
|
4040
|
+
const consumed = new Set();
|
|
4041
|
+
for (const p of target.properties) {
|
|
4042
|
+
if (ts.isShorthandPropertyAssignment(p))
|
|
4043
|
+
consumed.add(p.name.text);
|
|
4044
|
+
else if (ts.isPropertyAssignment(p)) {
|
|
4045
|
+
const n = patternKeyNameOf(L, p.name);
|
|
4046
|
+
if (n !== null)
|
|
4047
|
+
consumed.add(n);
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
if (!ts.isIdentifier(restTo)) {
|
|
4051
|
+
lowerAssignTargetInto(L, out, restTo, prop, null, (targetT) => classInstanceRestValue(L, prop, tmpRef, objT, info, consumed, targetT));
|
|
4052
|
+
continue;
|
|
4053
|
+
}
|
|
4054
|
+
const targetBinding = L.resolveWritable(restTo);
|
|
4055
|
+
if (!targetBinding) {
|
|
4056
|
+
L.rejectUnresolved(restTo, `assignment to '${restTo.text}' (not a writable local or module global)`);
|
|
4057
|
+
}
|
|
4058
|
+
const packed = classInstanceRestValue(L, prop, tmpRef, objT, info, consumed, targetBinding.type);
|
|
4059
|
+
out.push({ kind: "assign", localId: targetBinding.id, value: L.coerceInto(prop, packed, targetBinding.type), loc: propLoc });
|
|
4060
|
+
continue;
|
|
4061
|
+
}
|
|
4062
|
+
let fieldName;
|
|
4063
|
+
let bindTo;
|
|
4064
|
+
let dfltInit = null;
|
|
4065
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
4066
|
+
fieldName = prop.name.text;
|
|
4067
|
+
bindTo = prop.name;
|
|
4068
|
+
dfltInit = prop.objectAssignmentInitializer ?? null;
|
|
4069
|
+
}
|
|
4070
|
+
else if (ts.isPropertyAssignment(prop)) {
|
|
4071
|
+
const folded = patternKeyNameOf(L, prop.name);
|
|
4072
|
+
if (folded === null) {
|
|
4073
|
+
L.unsupported("SC1031", prop, "destructuring assignment with computed keys that do not fold to one property name");
|
|
4074
|
+
}
|
|
4075
|
+
fieldName = folded;
|
|
4076
|
+
bindTo = prop.initializer;
|
|
4077
|
+
if (ts.isBinaryExpression(bindTo) && bindTo.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
4078
|
+
dfltInit = bindTo.right;
|
|
4079
|
+
bindTo = bindTo.left;
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
else {
|
|
4083
|
+
L.unsupported("SC1031", prop, "destructuring assignment with getter/setter or method properties");
|
|
4084
|
+
}
|
|
4085
|
+
const dflt = dfltInit;
|
|
4086
|
+
const readOf = (targetT) => {
|
|
4087
|
+
const fieldType = info.fields.get(fieldName);
|
|
4088
|
+
const getF = fieldType === undefined ? L.findMethodOn(info, `get:${fieldName}`) : null;
|
|
4089
|
+
const setF = fieldType === undefined ? L.findMethodOn(info, `set:${fieldName}`) : null;
|
|
4090
|
+
let v;
|
|
4091
|
+
if (fieldType !== undefined) {
|
|
4092
|
+
v = L.fieldGetExpr({ container: "class", obj: tmpRef(), className: objT.className, field: fieldName, fieldType }, propLoc, prop);
|
|
4093
|
+
}
|
|
4094
|
+
else if (getF || setF) {
|
|
4095
|
+
v = L.fieldGetExpr({ container: "accessor", obj: tmpRef(), className: objT.className, field: fieldName, fieldType: getF ? getF.sig.ret : setF.sig.params[0].type }, propLoc, prop);
|
|
4096
|
+
// A defaulted getter result temps first — the default's
|
|
4097
|
+
// ternary mentions its operand twice, and JS calls the
|
|
4098
|
+
// getter once per element.
|
|
4099
|
+
if (dflt && targetT) {
|
|
4100
|
+
const got = L.declareHiddenLocal("%dget", v.type);
|
|
4101
|
+
out.push({ kind: "varDecl", localId: got.id, init: v, loc: propLoc });
|
|
4102
|
+
v = { kind: "varRef", localId: got.id, type: v.type, loc: propLoc };
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
else {
|
|
4106
|
+
L.unsupported("SC1031", prop, L.findMethodOn(info, fieldName)
|
|
4107
|
+
? `destructuring the method '${fieldName}' (a detached method loses its receiver — call it through the instance)`
|
|
4108
|
+
: `destructuring the property '${fieldName}' the class '${info.def.jsName ?? info.def.name}' does not declare`);
|
|
4109
|
+
}
|
|
4110
|
+
if (dflt && targetT)
|
|
4111
|
+
v = undefArmDefault(L, prop, dflt, v, targetT);
|
|
4112
|
+
return v;
|
|
4113
|
+
};
|
|
4114
|
+
while (ts.isParenthesizedExpression(bindTo))
|
|
4115
|
+
bindTo = bindTo.expression;
|
|
4116
|
+
if (!ts.isIdentifier(bindTo)) {
|
|
4117
|
+
lowerAssignTargetInto(L, out, bindTo, prop, dfltInit, readOf);
|
|
4118
|
+
continue;
|
|
4119
|
+
}
|
|
4120
|
+
let targetBinding;
|
|
4121
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
4122
|
+
const valueSymbol = L.checker.getShorthandAssignmentValueSymbol(prop) ?? null;
|
|
4123
|
+
const local = valueSymbol ? L.resolveKey(valueSymbol, bindTo) : null;
|
|
4124
|
+
const g = valueSymbol ? L.globalsBySymbol.get(valueSymbol) : undefined;
|
|
4125
|
+
targetBinding = local ? { id: local.id, type: local.type } : g ? { id: g.id, type: g.type } : null;
|
|
4126
|
+
}
|
|
4127
|
+
else {
|
|
4128
|
+
targetBinding = L.resolveWritable(bindTo);
|
|
4129
|
+
}
|
|
4130
|
+
if (!targetBinding) {
|
|
4131
|
+
L.rejectUnresolved(bindTo, `assignment to '${bindTo.text}' (not a writable local or module global)`);
|
|
4132
|
+
}
|
|
4133
|
+
out.push({
|
|
4134
|
+
kind: "assign",
|
|
4135
|
+
localId: targetBinding.id,
|
|
4136
|
+
value: L.coerceInto(prop, readOf(targetBinding.type), targetBinding.type),
|
|
4137
|
+
loc: propLoc,
|
|
4138
|
+
});
|
|
4139
|
+
}
|
|
4140
|
+
return { stmts: out, value };
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
3894
4143
|
if (init.type.kind !== "record") {
|
|
3895
4144
|
L.unsupported("SC1031", blame, init.type.kind === "object"
|
|
3896
4145
|
? "destructuring assignment from class instances (read the fields directly — accessors make the desugar observable)"
|
|
@@ -3914,13 +4163,6 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
3914
4163
|
let restTo = prop.expression;
|
|
3915
4164
|
while (ts.isParenthesizedExpression(restTo))
|
|
3916
4165
|
restTo = restTo.expression;
|
|
3917
|
-
if (!ts.isIdentifier(restTo)) {
|
|
3918
|
-
L.unsupported("SC1031", prop, "destructuring assignment of rest into non-variable targets");
|
|
3919
|
-
}
|
|
3920
|
-
const targetBinding = L.resolveWritable(restTo);
|
|
3921
|
-
if (!targetBinding) {
|
|
3922
|
-
L.rejectUnresolved(restTo, `assignment to '${restTo.text}' (not a writable local or module global)`);
|
|
3923
|
-
}
|
|
3924
4166
|
if (shape.indexValue) {
|
|
3925
4167
|
L.unsupported("SC1031", prop, "rest bindings over index-signature shapes (the undeclared entries would need overflow packing)");
|
|
3926
4168
|
}
|
|
@@ -3945,6 +4187,16 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
3945
4187
|
type: { kind: "record", shapeId: restShapeId },
|
|
3946
4188
|
loc: locOf(prop),
|
|
3947
4189
|
};
|
|
4190
|
+
if (!ts.isIdentifier(restTo)) {
|
|
4191
|
+
// `({ a, ...box.rest } = src)` — the rest packs identically and
|
|
4192
|
+
// lands through the property/element write machinery.
|
|
4193
|
+
lowerAssignTargetInto(L, out, restTo, prop, null, () => packed);
|
|
4194
|
+
continue;
|
|
4195
|
+
}
|
|
4196
|
+
const targetBinding = L.resolveWritable(restTo);
|
|
4197
|
+
if (!targetBinding) {
|
|
4198
|
+
L.rejectUnresolved(restTo, `assignment to '${restTo.text}' (not a writable local or module global)`);
|
|
4199
|
+
}
|
|
3948
4200
|
out.push({
|
|
3949
4201
|
kind: "assign",
|
|
3950
4202
|
localId: targetBinding.id,
|
|
@@ -3981,7 +4233,33 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
3981
4233
|
while (ts.isParenthesizedExpression(bindTo))
|
|
3982
4234
|
bindTo = bindTo.expression;
|
|
3983
4235
|
if (!ts.isIdentifier(bindTo)) {
|
|
3984
|
-
|
|
4236
|
+
// Nested patterns and property/element targets: the element's
|
|
4237
|
+
// value (default applied at the target's own type) lands through
|
|
4238
|
+
// the shared non-variable machinery.
|
|
4239
|
+
const fieldT = shape.fields.find((f) => f.name === fieldName)?.type;
|
|
4240
|
+
const dflt = dfltInit;
|
|
4241
|
+
lowerAssignTargetInto(L, out, bindTo, prop, dflt, (targetT) => {
|
|
4242
|
+
if (!fieldT) {
|
|
4243
|
+
// The defaulted read of a field the shape does not carry is
|
|
4244
|
+
// always undefined — the default IS the value (the identifier
|
|
4245
|
+
// path's rule below).
|
|
4246
|
+
if (dflt && targetT)
|
|
4247
|
+
return L.lowerExprExpecting(dflt, targetT);
|
|
4248
|
+
L.unsupported("SC1031", prop, `destructuring the field '${fieldName}' the source shape does not carry`);
|
|
4249
|
+
}
|
|
4250
|
+
let v = {
|
|
4251
|
+
kind: "recordGet",
|
|
4252
|
+
obj: { kind: "varRef", localId: tmp.id, type: srcType, loc: locOf(prop) },
|
|
4253
|
+
shapeId: srcType.shapeId,
|
|
4254
|
+
field: fieldName,
|
|
4255
|
+
type: fieldT,
|
|
4256
|
+
loc: locOf(prop),
|
|
4257
|
+
};
|
|
4258
|
+
if (dflt && targetT)
|
|
4259
|
+
v = undefArmDefault(L, prop, dflt, v, targetT);
|
|
4260
|
+
return v;
|
|
4261
|
+
});
|
|
4262
|
+
continue;
|
|
3985
4263
|
}
|
|
3986
4264
|
// Shorthand names resolve to the PROPERTY symbol at their location;
|
|
3987
4265
|
// the VALUE symbol (the binding being assigned) comes from the
|
|
@@ -4037,6 +4315,102 @@ function destructuringAssignInto(L, target, init, rhs, blame, loc) {
|
|
|
4037
4315
|
}
|
|
4038
4316
|
return { stmts: out, value };
|
|
4039
4317
|
}
|
|
4318
|
+
/** A destructuring-ASSIGNMENT target that is not a plain variable:
|
|
4319
|
+
* PROPERTY targets (`({ a: this.x } = o)`, `[c.x, c.y] = t` — the
|
|
4320
|
+
* receiver evaluates into a hidden temp at the element's pattern
|
|
4321
|
+
* position, BEFORE the element's value read, JS's get-the-reference-
|
|
4322
|
+
* then-GetV order), ELEMENT targets over arrays (runtime f64 index)
|
|
4323
|
+
* and tuples/records (literal keys) with the same receiver/key temp
|
|
4324
|
+
* discipline, and NESTED patterns (`({ p: { q } } = o)`, `[[a], b] = t`
|
|
4325
|
+
* — the element's value destructures through the same assignment
|
|
4326
|
+
* machinery, its own hidden temp included). `valueOf` builds the
|
|
4327
|
+
* (defaulted) source value at the target's own type; it receives null
|
|
4328
|
+
* exactly for nested patterns, which have no single binding type —
|
|
4329
|
+
* defaults there keep a fence (JS would need the pattern's implied
|
|
4330
|
+
* type to test against). Optional-chain targets are not assignment
|
|
4331
|
+
* targets in JS at all; index-signature runtime keys and island/dyn
|
|
4332
|
+
* receivers keep the fence below. */
|
|
4333
|
+
function lowerAssignTargetInto(L, out, targetNode, blame, dflt, valueOf) {
|
|
4334
|
+
let t = targetNode;
|
|
4335
|
+
while (ts.isParenthesizedExpression(t))
|
|
4336
|
+
t = t.expression;
|
|
4337
|
+
const loc = locOf(t);
|
|
4338
|
+
if (ts.isObjectLiteralExpression(t) || ts.isArrayLiteralExpression(t)) {
|
|
4339
|
+
if (dflt) {
|
|
4340
|
+
L.unsupported("SC1031", blame, "defaults on nested patterns in destructuring assignment (bind a variable with the default, then destructure it)");
|
|
4341
|
+
}
|
|
4342
|
+
const sub = destructuringAssignInto(L, t, valueOf(null), null, blame, loc);
|
|
4343
|
+
out.push(...sub.stmts);
|
|
4344
|
+
return;
|
|
4345
|
+
}
|
|
4346
|
+
if (ts.isPropertyAccessExpression(t) && !t.questionDotToken) {
|
|
4347
|
+
const ft = L.fieldTarget(t);
|
|
4348
|
+
if (ft) {
|
|
4349
|
+
// The receiver temps FIRST: JS evaluates the target reference
|
|
4350
|
+
// before reading the element's value, so a side-effecting
|
|
4351
|
+
// receiver (`f().x`) must run before the source read the
|
|
4352
|
+
// defaulted value machinery may emit.
|
|
4353
|
+
const recv = L.declareHiddenLocal("%dtRecv", ft.obj.type);
|
|
4354
|
+
out.push({ kind: "varDecl", localId: recv.id, init: ft.obj, loc });
|
|
4355
|
+
const value = L.coerceInto(blame, valueOf(ft.fieldType), ft.fieldType);
|
|
4356
|
+
out.push(L.fieldSetStmt({ ...ft, obj: { kind: "varRef", localId: recv.id, type: ft.obj.type, loc } }, value, loc, t));
|
|
4357
|
+
return;
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
if (ts.isElementAccessExpression(t) && !t.questionDotToken) {
|
|
4361
|
+
const recvT = L.mapTypeOf(L.typeOf(t.expression));
|
|
4362
|
+
if (recvT?.kind === "array") {
|
|
4363
|
+
const arr = L.lowerExpr(t.expression);
|
|
4364
|
+
if (arr.type.kind === "array") {
|
|
4365
|
+
const recv = L.declareHiddenLocal("%dtRecv", arr.type);
|
|
4366
|
+
out.push({ kind: "varDecl", localId: recv.id, init: arr, loc });
|
|
4367
|
+
const index = L.lowerExpr(t.argumentExpression);
|
|
4368
|
+
if (index.type.kind !== "f64") {
|
|
4369
|
+
L.unsupported("SC1090", t.argumentExpression, "indexing with non-number keys");
|
|
4370
|
+
}
|
|
4371
|
+
const idx = L.declareHiddenLocal("%dtIdx", F64);
|
|
4372
|
+
out.push({ kind: "varDecl", localId: idx.id, init: index, loc });
|
|
4373
|
+
const value = L.coerceInto(blame, valueOf(recvT.elem), recvT.elem);
|
|
4374
|
+
out.push({
|
|
4375
|
+
kind: "arraySet",
|
|
4376
|
+
arr: { kind: "varRef", localId: recv.id, type: arr.type, loc },
|
|
4377
|
+
index: { kind: "varRef", localId: idx.id, type: F64, loc },
|
|
4378
|
+
value,
|
|
4379
|
+
loc,
|
|
4380
|
+
});
|
|
4381
|
+
return;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
else if (recvT?.kind === "record") {
|
|
4385
|
+
// Tuple positions and LITERAL declared record keys are field
|
|
4386
|
+
// writes (the element-write lowering's own rule); runtime keys
|
|
4387
|
+
// keep the fence below.
|
|
4388
|
+
const shape = L.shapes.get(recvT.shapeId);
|
|
4389
|
+
const litKey = ts.isStringLiteralLike(t.argumentExpression) || ts.isNumericLiteral(t.argumentExpression)
|
|
4390
|
+
? L.foldedStringKeyOf(t.argumentExpression)
|
|
4391
|
+
: null;
|
|
4392
|
+
const field = litKey !== null ? shape?.fields.find((f) => f.name === litKey) : undefined;
|
|
4393
|
+
if (field) {
|
|
4394
|
+
const obj = L.lowerExpr(t.expression);
|
|
4395
|
+
if (obj.type.kind === "record") {
|
|
4396
|
+
const recv = L.declareHiddenLocal("%dtRecv", obj.type);
|
|
4397
|
+
out.push({ kind: "varDecl", localId: recv.id, init: obj, loc });
|
|
4398
|
+
const value = L.coerceInto(blame, valueOf(field.type), field.type);
|
|
4399
|
+
out.push({
|
|
4400
|
+
kind: "recordSet",
|
|
4401
|
+
obj: { kind: "varRef", localId: recv.id, type: obj.type, loc },
|
|
4402
|
+
shapeId: recvT.shapeId,
|
|
4403
|
+
field: field.name,
|
|
4404
|
+
value,
|
|
4405
|
+
loc,
|
|
4406
|
+
});
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
L.unsupported("SC1031", targetNode, "destructuring assignment to targets with no static write form (assign a variable, then write the parts out)");
|
|
4413
|
+
}
|
|
4040
4414
|
/** One object-pattern property's pieces for the checked-dynamic path:
|
|
4041
4415
|
* the source FIELD name (or, over ISLAND sources, a runtime KEY
|
|
4042
4416
|
* expression the engine indexes with) and the resolved writable target.
|
|
@@ -4184,32 +4558,28 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
|
4184
4558
|
}
|
|
4185
4559
|
while (ts.isParenthesizedExpression(targetNode))
|
|
4186
4560
|
targetNode = targetNode.expression;
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
// IS the assignment (JS evaluates it unconditionally there).
|
|
4203
|
-
read = L.lowerExprExpecting(defaultNode, targetBinding.type);
|
|
4204
|
-
}
|
|
4205
|
-
else {
|
|
4561
|
+
// The element's (defaulted) value at the target's own type — shared
|
|
4562
|
+
// by variable, property/element, and nested-pattern targets (the
|
|
4563
|
+
// latter receive null: no single binding type exists there, and
|
|
4564
|
+
// defaults on nested targets fence in the shared machinery).
|
|
4565
|
+
const readOf = (targetT) => {
|
|
4566
|
+
if (isRest) {
|
|
4567
|
+
return isTuple
|
|
4568
|
+
? tupleTailValue(L, el, tmpRef, srcType, shape, i, targetT)
|
|
4569
|
+
: { kind: "arrIntrinsic", method: "slice", receiver: tmpRef(), args: [{ kind: "numLit", value: i, type: F64, loc: locOf(el) }], type: srcType, loc: locOf(el) };
|
|
4570
|
+
}
|
|
4571
|
+
if (isTuple && defaultNode !== null && targetT !== null && !shape.fields.some((f) => f.name === String(i))) {
|
|
4572
|
+
// Past the tuple's end the read is always undefined — the default
|
|
4573
|
+
// IS the assignment (JS evaluates it unconditionally there).
|
|
4574
|
+
return L.lowerExprExpecting(defaultNode, targetT);
|
|
4575
|
+
}
|
|
4206
4576
|
// Defaults: JS applies the default ONLY when the element reads
|
|
4207
4577
|
// undefined. A checked-dynamic element tests at runtime; a tuple
|
|
4208
4578
|
// position tests its undefined arm (a never-undefined field makes
|
|
4209
4579
|
// the default dead — JS would not evaluate it either); an array
|
|
4210
4580
|
// position carries the bounds test (past the end JS reads
|
|
4211
4581
|
// undefined where the plain read would trap).
|
|
4212
|
-
read = elemRead(i, el);
|
|
4582
|
+
let read = elemRead(i, el);
|
|
4213
4583
|
if (defaultNode !== null) {
|
|
4214
4584
|
if (read.type.kind === "dyn" || read.type.kind === "jsval") {
|
|
4215
4585
|
const boundary = read.type;
|
|
@@ -4229,18 +4599,26 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
|
|
|
4229
4599
|
loc: locOf(el),
|
|
4230
4600
|
};
|
|
4231
4601
|
}
|
|
4232
|
-
else if (
|
|
4233
|
-
read =
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
read = arrayPositionDefaultValue(L, el, defaultNode, read, tmpRef, i, srcType.elem, targetBinding.type, out);
|
|
4602
|
+
else if (targetT !== null) {
|
|
4603
|
+
read = isTuple
|
|
4604
|
+
? undefArmDefault(L, el, defaultNode, read, targetT)
|
|
4605
|
+
: arrayPositionDefaultValue(L, el, defaultNode, read, tmpRef, i, srcType.elem, targetT, out);
|
|
4237
4606
|
}
|
|
4238
4607
|
}
|
|
4608
|
+
return read;
|
|
4609
|
+
};
|
|
4610
|
+
if (!ts.isIdentifier(targetNode)) {
|
|
4611
|
+
lowerAssignTargetInto(L, out, targetNode, el, defaultNode, readOf);
|
|
4612
|
+
return;
|
|
4613
|
+
}
|
|
4614
|
+
const targetBinding = L.resolveWritable(targetNode);
|
|
4615
|
+
if (!targetBinding) {
|
|
4616
|
+
L.rejectUnresolved(targetNode, `assignment to '${targetNode.text}' (not a writable local or module global)`);
|
|
4239
4617
|
}
|
|
4240
4618
|
out.push({
|
|
4241
4619
|
kind: "assign",
|
|
4242
4620
|
localId: targetBinding.id,
|
|
4243
|
-
value: L.coerceInto(el,
|
|
4621
|
+
value: L.coerceInto(el, readOf(targetBinding.type), targetBinding.type),
|
|
4244
4622
|
loc: locOf(el),
|
|
4245
4623
|
});
|
|
4246
4624
|
});
|
|
@@ -4371,7 +4749,7 @@ export function lowerForOf(L, stmt) {
|
|
|
4371
4749
|
if (iterable.type.kind !== "array") {
|
|
4372
4750
|
// The lib types strings as iterable too, so those for-ofs typecheck;
|
|
4373
4751
|
// only arrays, Maps, and Sets have a lowering. Named specifically —
|
|
4374
|
-
// the
|
|
4752
|
+
// the blanket type fence would blame the TYPE, which is itself supported.
|
|
4375
4753
|
if (iterable.type.kind === "string") {
|
|
4376
4754
|
// JS's string iterator walks code POINTS, not units: each pass
|
|
4377
4755
|
// yields the whole character (two UTF-16 units for astral chars,
|