@scriptc/compiler 0.0.36 → 0.1.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/ambient/scriptc-node-fallback.d.ts +7 -5
- package/ambient/scriptc-overrides.d.ts +0 -7
- package/ambient/scriptc.d.ts +1 -1
- package/dist/backend/c/exprs.js +69 -45
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/may-throw.js +3 -0
- package/dist/backend/c/may-throw.js.map +1 -1
- package/dist/backend/c/stmts.js +18 -0
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/walkers.js +5 -7
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/external-c.d.ts +33 -0
- package/dist/backend/external-c.js +51 -0
- package/dist/backend/external-c.js.map +1 -0
- package/dist/backend/link-plan.d.ts +37 -0
- package/dist/backend/link-plan.js +26 -0
- package/dist/backend/link-plan.js.map +1 -0
- package/dist/backend/linker.d.ts +22 -0
- package/dist/backend/linker.js +236 -0
- package/dist/backend/linker.js.map +1 -0
- package/dist/backend/llvm/dyn.js +7 -0
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +34 -7
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +68 -54
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +1 -1
- package/dist/backend/llvm/expr-dispatch.js +2 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +20 -0
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +25 -1
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/walkers.js +31 -4
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +8 -1
- package/dist/backend/native-codegen.js +39 -25
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +42 -4
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +1 -1
- package/dist/backend/native-link-info.js +42 -18
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +5 -1
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.d.ts +1 -0
- package/dist/backend/runtime-pack-targets.test.js +54 -0
- package/dist/backend/runtime-pack-targets.test.js.map +1 -0
- package/dist/backend/runtime-pack.d.ts +5 -30
- package/dist/backend/runtime-pack.js +46 -141
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +49 -14
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +63 -17
- package/dist/backend/targets.js +251 -35
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +27 -5
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/coverage/surface-manifest.js +10 -1
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +8 -0
- package/dist/frontend/lowering/array-values.js +60 -0
- package/dist/frontend/lowering/array-values.js.map +1 -0
- package/dist/frontend/lowering/lower-array-sort.d.ts +22 -0
- package/dist/frontend/lowering/lower-array-sort.js +619 -0
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.js +164 -4
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +3 -4
- package/dist/frontend/lowering/lower-calls.js +260 -27
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +728 -655
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +4 -0
- package/dist/frontend/lowering/lower-exprs.js +769 -101
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +47 -33
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +5 -4
- package/dist/frontend/lowering/lower-island.js +10 -5
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-server.js +10 -0
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +302 -71
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +74 -2
- package/dist/frontend/lowering/lowerer.js +1087 -10
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +10 -2
- package/dist/frontend/lowering/surfaces.js +15 -3
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +45 -23
- package/dist/index.js.map +1 -1
- package/dist/ir/ir.d.ts +65 -11
- package/dist/ir/ir.js +25 -25
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +84 -37
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/fence-eval.js +3 -1
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.js +12 -1
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/library-cache.test.js +5 -5
- package/dist/startup-cache.d.ts +9 -1
- package/dist/startup-cache.js +16 -1
- package/dist/startup-cache.js.map +1 -1
- package/package.json +17 -4
- package/surface-manifest.json +5 -5
|
@@ -4,6 +4,7 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
4
4
|
* flow (if/while/for/for-of/do, switch, try/catch, jumps with the
|
|
5
5
|
* finally-crossing fence), and blocked-binding poisoning. */
|
|
6
6
|
import * as ts from "../ts7/adapter.js";
|
|
7
|
+
import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
|
|
7
8
|
import { lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
|
|
8
9
|
import { BOOL, BYTES_U8, CAUGHT, DYN, F64, JSVAL, STRING, UNDEFINED_T, VOID, arrayOf, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
|
|
9
10
|
import { PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, neverTaintedJsType, stmtUsesIsland, uncheckedOverloadHandleCall } from "./lowerer.js";
|
|
@@ -13,7 +14,7 @@ import { COMPOUND_ASSIGN_OPS, STR_METHODS, UNSUPPORTED_STMT, isStdlibMember, sid
|
|
|
13
14
|
import { isProvenanceSourceFile } from "../provenance-registry.js";
|
|
14
15
|
import { ambientUndefVarRootOf, lowerImportEquals, nsUndefRead, nsWritableTarget, trapDeclRootOf } from "./lower-namespaces.js";
|
|
15
16
|
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
|
16
|
-
import { lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
|
17
|
+
import { lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, lowerSafeIndexRead, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
|
17
18
|
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
18
19
|
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
19
20
|
import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
|
|
@@ -21,13 +22,13 @@ import { lowerStreamUnderscoreAssign, streamClassAliasDecl, streamSidesOf } from
|
|
|
21
22
|
import { lowerHttpResPropertyAssignment, lowerHttpServerTimeoutAssignment, lowerServerCloseOverrideAssignment } from "./lower-server.js";
|
|
22
23
|
import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf, createRequireBindingDecl, createRequireCalleeFileOf, createRequireNamespaceDecl, textCodecBindingDecl } from "./lower-builtins.js";
|
|
23
24
|
import { lowerEnumDeclaration } from "./lower-enums.js";
|
|
24
|
-
import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, matchResultNamedGroupsOf, probeLower, pureReemittable, runtimeOptionalTrueIds, symbolFieldInfo, withRuntimeOptionalNarrowed } from "./lower-exprs.js";
|
|
25
|
+
import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf, probeLower, pureReemittable, runtimeOptionalTrueIds, symbolFieldInfo, withRuntimeOptionalNarrowed } from "./lower-exprs.js";
|
|
25
26
|
import { checkerPanicDiag, isCheckerPanic, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
26
27
|
import { isParseArgsDynTypeName, isUnitOnlyTsType, unitOnlyUnion } from "../type-mapper.js";
|
|
27
28
|
import { canonicalBuiltinModule } from "../builtin-modules.js";
|
|
28
29
|
import { isRelativeSpecifier } from "../workspace-registry.js";
|
|
29
30
|
import { probeNodeRequireRefusal } from "../npm.js";
|
|
30
|
-
import { varRef } from "../../ir/build.js";
|
|
31
|
+
import { numLit, varRef } from "../../ir/build.js";
|
|
31
32
|
/** `const X = /* @__PURE__ *\/ makeX()` at a module's top level: every
|
|
32
33
|
* declarator initialized by a call (or `new`) carrying the bundler PURE
|
|
33
34
|
* annotation in its leading trivia — the shape --provenance-sources may
|
|
@@ -1264,6 +1265,11 @@ export function lowerDestructuringDecl(lowerer, decl, isLet) {
|
|
|
1264
1265
|
/** One pattern level: emits a declaration (or global assignment) per
|
|
1265
1266
|
* bound name, recursing through hidden temps for nested patterns. */
|
|
1266
1267
|
export function lowerBindingPattern(lowerer, pattern, srcRef, srcType, isLet, out, dynSpell, allowDynObject = false) {
|
|
1268
|
+
const present = patternSourceValue(lowerer, srcRef(), ts.isArrayBindingPattern(pattern), out, locOf(pattern), dynSpell);
|
|
1269
|
+
if (!typeEquals(present.type, srcType)) {
|
|
1270
|
+
lowerer.lowerBindingPattern(pattern, () => present, present.type, isLet, out, dynSpell, allowDynObject);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1267
1273
|
if (ts.isArrayBindingPattern(pattern)) {
|
|
1268
1274
|
lowerer.fenceStaticHeadersIteration(pattern);
|
|
1269
1275
|
// Tuple sources: each position is a field read of the tuple's record
|
|
@@ -1500,29 +1506,22 @@ export function lowerBindingPattern(lowerer, pattern, srcRef, srcType, isLet, ou
|
|
|
1500
1506
|
if (ts.isOmittedExpression(el) || el.name === undefined)
|
|
1501
1507
|
return; // hole: position skipped, like JS over an array
|
|
1502
1508
|
const loc = locOf(el);
|
|
1503
|
-
//
|
|
1504
|
-
//
|
|
1505
|
-
//
|
|
1506
|
-
// and a NESTED rest pattern (`[...[a, b]]`) destructures the copy.
|
|
1509
|
+
// Array rest drains values from the iterator, so holes become
|
|
1510
|
+
// present undefined in the fresh tail. Nested rest patterns then
|
|
1511
|
+
// destructure that materialized copy.
|
|
1507
1512
|
if (el.dotDotDotToken) {
|
|
1508
1513
|
if (el.initializer) {
|
|
1509
1514
|
lowerer.unsupported("SC1031", el, "defaults on rest elements"); // tsc rejects; defensive
|
|
1510
1515
|
}
|
|
1511
|
-
const value =
|
|
1512
|
-
kind: "arrIntrinsic",
|
|
1513
|
-
method: "slice",
|
|
1514
|
-
receiver: srcRef(),
|
|
1515
|
-
args: [{ kind: "numLit", value: i, type: F64, loc }],
|
|
1516
|
-
type: srcType,
|
|
1517
|
-
loc,
|
|
1518
|
-
};
|
|
1516
|
+
const value = arrayRestValue(srcRef(), i, srcType, loc);
|
|
1519
1517
|
lowerer.bindPatternTarget(el.name, value, isLet, out);
|
|
1520
1518
|
return;
|
|
1521
1519
|
}
|
|
1522
|
-
|
|
1520
|
+
const position = { kind: "numLit", value: i, type: F64, loc };
|
|
1521
|
+
let value = lowerSafeIndexRead(lowerer, srcRef(), position, loc) ?? {
|
|
1523
1522
|
kind: "arrayGet",
|
|
1524
1523
|
arr: srcRef(),
|
|
1525
|
-
index:
|
|
1524
|
+
index: position,
|
|
1526
1525
|
type: srcType.elem,
|
|
1527
1526
|
loc,
|
|
1528
1527
|
};
|
|
@@ -1842,6 +1841,33 @@ export function lowerBindingPattern(lowerer, pattern, srcRef, srcType, isLet, ou
|
|
|
1842
1841
|
lowerer.bindPatternTarget(el.name, value, isLet, out);
|
|
1843
1842
|
}
|
|
1844
1843
|
}
|
|
1844
|
+
/** Destructuring a missing array element throws before reading members.
|
|
1845
|
+
* Only unwrap a union after testing every nullish arm; this also keeps
|
|
1846
|
+
* dense record/tuple callbacks on their existing payload representation. */
|
|
1847
|
+
function patternSourceValue(lowerer, value, arrayPattern, out, loc, spelling) {
|
|
1848
|
+
if (value.type.kind !== "union")
|
|
1849
|
+
return value;
|
|
1850
|
+
const unionId = value.type.unionId;
|
|
1851
|
+
const arms = lowerer.unions.get(unionId)?.arms;
|
|
1852
|
+
const present = arms?.filter((arm) => !isUnitType(arm));
|
|
1853
|
+
if (!arms || present?.length !== 1 || arms.length === 1)
|
|
1854
|
+
return value;
|
|
1855
|
+
for (const [tag, arm] of arms.entries()) {
|
|
1856
|
+
if (!isUnitType(arm))
|
|
1857
|
+
continue;
|
|
1858
|
+
const unit = arm.kind === "undefinedT" ? "undefined" : "null";
|
|
1859
|
+
const message = arrayPattern
|
|
1860
|
+
? spelling ?? `${unit} is not iterable (cannot read property Symbol(Symbol.iterator))`
|
|
1861
|
+
: `Cannot destructure ${unit}`;
|
|
1862
|
+
out.push({
|
|
1863
|
+
kind: "if",
|
|
1864
|
+
cond: { kind: "unionIsTag", unionId, tag, value, negated: false, type: BOOL, loc },
|
|
1865
|
+
then: [{ kind: "throw", value: { kind: "libCall", fn: "error.new", args: [{ kind: "strLit", value: message, type: STRING, loc }], type: { kind: "object", className: "%TypeError" }, loc }, loc }],
|
|
1866
|
+
else_: null, loc,
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
return { kind: "unionNarrow", unionId, tag: lowerer.armTag(unionId, present[0]), value, type: present[0], loc };
|
|
1870
|
+
}
|
|
1845
1871
|
/** The type a pattern element binds at — the checker's type of the bound
|
|
1846
1872
|
* identifier, or of the nested pattern itself (its implied type). Null
|
|
1847
1873
|
* when the type has no static mapping (the caller fences). */
|
|
@@ -2138,13 +2164,9 @@ function undefArmDefault(lowerer, blame, init, value, bodyT) {
|
|
|
2138
2164
|
const dflt = lowerer.lowerExprExpecting(init, bodyT);
|
|
2139
2165
|
return { kind: "ternary", cond: isUndef, then: dflt, else_: present, type: bodyT, loc };
|
|
2140
2166
|
}
|
|
2141
|
-
/**
|
|
2142
|
-
*
|
|
2143
|
-
*
|
|
2144
|
-
* where the plain read would trap (the array divergence's carve-out).
|
|
2145
|
-
* Elements with an undefined arm land in a hidden temp first (in-bounds
|
|
2146
|
-
* undefined and past-the-end collapse to the same arm), then the
|
|
2147
|
-
* ordinary undefined-arm default applies. */
|
|
2167
|
+
/** An array binding default tests the value produced by the read. Holes,
|
|
2168
|
+
* out-of-range positions, and explicit undefined share the same arm.
|
|
2169
|
+
* Typed-array reads retain the separate bounds check below. */
|
|
2148
2170
|
function arrayPositionDefault(lowerer, el, read, srcRef, index, elemT, out) {
|
|
2149
2171
|
const name = el.name; // callers skip nameless elisions
|
|
2150
2172
|
const bodyT = patternBindingType(lowerer, name);
|
|
@@ -2157,6 +2179,11 @@ function arrayPositionDefault(lowerer, el, read, srcRef, index, elemT, out) {
|
|
|
2157
2179
|
* arrives from the existing binding instead of the checker. */
|
|
2158
2180
|
function arrayPositionDefaultValue(lowerer, blame, init, read, srcRef, index, elemT, bodyT, out) {
|
|
2159
2181
|
const loc = read.loc;
|
|
2182
|
+
if (read.type.kind === "union" && lowerer.armTag(read.type.unionId, UNDEFINED_T) >= 0) {
|
|
2183
|
+
const tmp = lowerer.declareHiddenLocal("%delem", read.type);
|
|
2184
|
+
out.push({ kind: "varDecl", localId: tmp.id, init: read, loc });
|
|
2185
|
+
return undefArmDefault(lowerer, blame, init, varRef(tmp.id, read.type, loc), bodyT);
|
|
2186
|
+
}
|
|
2160
2187
|
// The unit-only element (a declared-empty-tuple source riding the
|
|
2161
2188
|
// array representation): every read is undefined — the default IS the
|
|
2162
2189
|
// binding.
|
|
@@ -2213,6 +2240,15 @@ function arrayPositionDefaultValue(lowerer, blame, init, read, srcRef, index, el
|
|
|
2213
2240
|
loc,
|
|
2214
2241
|
};
|
|
2215
2242
|
}
|
|
2243
|
+
/** Array rest copies values and materializes holes. Removing the prefix
|
|
2244
|
+
* through the copying splice primitive preserves the payload ABI. */
|
|
2245
|
+
function arrayRestValue(receiver, from, type, loc) {
|
|
2246
|
+
return {
|
|
2247
|
+
kind: "arrIntrinsic", method: "toSpliced", receiver,
|
|
2248
|
+
args: [numLit(0, loc), numLit(from, loc), { kind: "arrayLit", elems: [], type, loc }],
|
|
2249
|
+
type, loc,
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2216
2252
|
/** The fresh tail for `[head, ...rest]` over a TUPLE source, packed under
|
|
2217
2253
|
* the checker's own rest type: an ARRAY of the remaining positions (each
|
|
2218
2254
|
* read coercing into the element type) or a tuple RECORD when the
|
|
@@ -2790,12 +2826,31 @@ export function bindPatternTarget(lowerer, name, value, isLet, out, allowDynObje
|
|
|
2790
2826
|
const mapped = lowerer.mapTypeOf(lowerer.typeOf(name));
|
|
2791
2827
|
return mapped === null || (lowerer.dynamic && jsvalFlavoredType(mapped));
|
|
2792
2828
|
})();
|
|
2793
|
-
|
|
2829
|
+
let type = dynStays ? DYN : lowerer.runtimeOptionalBindingType(name, lowerer.irTypeOf(name)) ?? lowerer.irTypeOf(name);
|
|
2794
2830
|
if (type.kind === "void")
|
|
2795
2831
|
lowerer.badType(name, lowerer.typeOf(name));
|
|
2832
|
+
// Array destructuring positions are ordinary indexed reads. When the
|
|
2833
|
+
// checker was built without noUncheckedIndexedAccess it still spells
|
|
2834
|
+
// the binding as the bare element type, but the runtime value carries
|
|
2835
|
+
// the same `T | undefined` possibility as a direct array read. Adopt
|
|
2836
|
+
// that union for an unannotated pattern binding so undefined survives
|
|
2837
|
+
// through later locals, templates, operators, and narrowing instead
|
|
2838
|
+
// of being extracted with a checked arm read.
|
|
2839
|
+
let runtimeOptional = false;
|
|
2840
|
+
if (!dynStays && value.type.kind === "union" &&
|
|
2841
|
+
lowerer.armTag(value.type.unionId, UNDEFINED_T) >= 0 &&
|
|
2842
|
+
typeEquals(lowerer.stripUndefinedArm(value.type), type)) {
|
|
2843
|
+
type = value.type;
|
|
2844
|
+
runtimeOptional = true;
|
|
2845
|
+
}
|
|
2796
2846
|
const coerced = lowerer.coerceInto(name, value, type);
|
|
2797
2847
|
const local = lowerer.declareLocal(name, name.text, type, isLet);
|
|
2798
2848
|
out.push({ kind: "varDecl", localId: local.id, init: coerced, loc });
|
|
2849
|
+
if (runtimeOptional) {
|
|
2850
|
+
const runtimeOptionalRoot = lowerer.runtimeOptionalRootOf(local);
|
|
2851
|
+
lowerer.runtimeOptionalLocals.add(runtimeOptionalRoot);
|
|
2852
|
+
lowerer.runtimeOptionalStorageLocals.add(runtimeOptionalRoot);
|
|
2853
|
+
}
|
|
2799
2854
|
return;
|
|
2800
2855
|
}
|
|
2801
2856
|
const tmp = lowerer.declareHiddenLocal("%destr", value.type);
|
|
@@ -3026,8 +3081,23 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3026
3081
|
// (statics are zero/NULL-initialized; uninitialized globals need no
|
|
3027
3082
|
// statement at all).
|
|
3028
3083
|
const declSymbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
3029
|
-
const g =
|
|
3084
|
+
const g = ts.isIdentifier(decl.name)
|
|
3085
|
+
? lowerer.globalOf(decl.name)
|
|
3086
|
+
: (declSymbol ? lowerer.globalsBySymbol.get(declSymbol) : undefined);
|
|
3030
3087
|
if (g) {
|
|
3088
|
+
let promotedGlobal = lowerer.runtimeOptionalBindingType(decl.name, g.type);
|
|
3089
|
+
if (g.type.kind === "func" && decl.initializer !== undefined && ts.isIdentifier(decl.initializer)) {
|
|
3090
|
+
const sig = lowerer.fnSigOf(decl.initializer);
|
|
3091
|
+
if (sig) {
|
|
3092
|
+
promotedGlobal = {
|
|
3093
|
+
...g.type,
|
|
3094
|
+
params: sig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
3095
|
+
ret: sig.returnType,
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
if (!typeEquals(promotedGlobal, g.type))
|
|
3100
|
+
g.type = promotedGlobal;
|
|
3031
3101
|
if (!decl.initializer) {
|
|
3032
3102
|
// `let x: string | undefined;` at file scope: READABLE before any
|
|
3033
3103
|
// assignment — JS gives undefined, so the slot must hold the
|
|
@@ -3048,7 +3118,16 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3048
3118
|
return { kind: "assign", localId: g.id, value: wrapped, loc: locOf(decl) };
|
|
3049
3119
|
return null;
|
|
3050
3120
|
}
|
|
3051
|
-
const
|
|
3121
|
+
const raw = lowerVariableInitializer(lowerer, decl.initializer);
|
|
3122
|
+
const arithmetic = raw.type.kind === "union"
|
|
3123
|
+
? lowerer.unions.get(raw.type.unionId)?.arms
|
|
3124
|
+
: undefined;
|
|
3125
|
+
if (g.type.kind === "string" && arithmetic?.length === 2 &&
|
|
3126
|
+
arithmetic.some((a) => a.kind === "f64") && arithmetic.some((a) => a.kind === "string")) {
|
|
3127
|
+
g.type = raw.type;
|
|
3128
|
+
lowerer.runtimeOptionalArithmeticGlobals.add(g);
|
|
3129
|
+
}
|
|
3130
|
+
const init = lowerer.coerceInto(decl.initializer, raw, g.type);
|
|
3052
3131
|
return { kind: "assign", localId: g.id, value: init, loc: locOf(decl) };
|
|
3053
3132
|
}
|
|
3054
3133
|
// A forward-captured const pre-declared as a TDZ box (an earlier
|
|
@@ -3108,7 +3187,7 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3108
3187
|
try {
|
|
3109
3188
|
init = immediatelyGuardedAbsenceProbe(lowerer, decl)
|
|
3110
3189
|
? (lowerAbsenceProbe(lowerer, decl.initializer) ?? lowerer.lowerExpr(decl.initializer))
|
|
3111
|
-
: lowerer
|
|
3190
|
+
: lowerVariableInitializer(lowerer, decl.initializer);
|
|
3112
3191
|
}
|
|
3113
3192
|
catch (e) {
|
|
3114
3193
|
if (e instanceof PoisonError) {
|
|
@@ -3194,12 +3273,30 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3194
3273
|
// and TypeScript's fresh-array probe idioms; an explicit annotation keeps
|
|
3195
3274
|
// the caller's checked slot contract.
|
|
3196
3275
|
let runtimeOptional = false;
|
|
3197
|
-
if (
|
|
3276
|
+
if (type !== null && init.type.kind === "union" &&
|
|
3277
|
+
lowerer.armTag(init.type.unionId, UNDEFINED_T) >= 0) {
|
|
3278
|
+
const present = lowerer.stripUndefinedArm(init.type);
|
|
3279
|
+
if (!typeEquals(present, type) && lowerer.widthLiftPlan(present, type) !== null) {
|
|
3280
|
+
const widened = type.kind === "union" ? lowerer.withUndefinedArmOf(type) : lowerer.withUndefinedArm(type);
|
|
3281
|
+
if (widened && lowerer.widthLiftPlan(init.type, widened) !== null) {
|
|
3282
|
+
type = widened;
|
|
3283
|
+
runtimeOptional = true;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
if (type !== null && init.type.kind === "union" &&
|
|
3198
3288
|
lowerer.armTag(init.type.unionId, UNDEFINED_T) >= 0 &&
|
|
3199
3289
|
typeEquals(lowerer.stripUndefinedArm(init.type), type)) {
|
|
3200
3290
|
type = init.type;
|
|
3201
3291
|
runtimeOptional = true;
|
|
3202
3292
|
}
|
|
3293
|
+
if (type?.kind === "array" && init.type.kind === "array" &&
|
|
3294
|
+
init.type.elem.kind === "union" &&
|
|
3295
|
+
lowerer.armTag(init.type.elem.unionId, UNDEFINED_T) >= 0 &&
|
|
3296
|
+
typeEquals(lowerer.stripUndefinedArm(init.type.elem), type.elem)) {
|
|
3297
|
+
type = init.type;
|
|
3298
|
+
runtimeOptional = true;
|
|
3299
|
+
}
|
|
3203
3300
|
// A JS binding whose checker type spells a FUNCTION but whose VALUE is
|
|
3204
3301
|
// already checked-dynamic (`const cb = mustCall(fn)` — the helper's
|
|
3205
3302
|
// return rides its own box): keep the box. Extracting into the spelled
|
|
@@ -3241,6 +3338,20 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3241
3338
|
}
|
|
3242
3339
|
if (!type)
|
|
3243
3340
|
lowerer.badType(decl.name, lowerer.typeOf(decl.name));
|
|
3341
|
+
const arithmeticType = decl.initializer ? lowerer.runtimeOptionalArithmeticTypes.get(decl.initializer) : undefined;
|
|
3342
|
+
const isStringArithmeticUnion = (t) => {
|
|
3343
|
+
if (t.kind !== "union")
|
|
3344
|
+
return false;
|
|
3345
|
+
const arms = lowerer.unions.get(t.unionId)?.arms;
|
|
3346
|
+
return !!arms && arms.length === 2 &&
|
|
3347
|
+
arms.some((a) => a.kind === "f64") && arms.some((a) => a.kind === "string");
|
|
3348
|
+
};
|
|
3349
|
+
const runtimeStringArithmetic = type?.kind === "string" &&
|
|
3350
|
+
((arithmeticType && typeEquals(arithmeticType, init.type)) || isStringArithmeticUnion(init.type));
|
|
3351
|
+
if (runtimeStringArithmetic) {
|
|
3352
|
+
type = init.type;
|
|
3353
|
+
}
|
|
3354
|
+
type = lowerer.runtimeOptionalBindingType(decl.name, type);
|
|
3244
3355
|
// `const x: void = undefined` / `let y: undefined = undefined`: the
|
|
3245
3356
|
// unit-only union — the initializer's unit literal wraps into its arm
|
|
3246
3357
|
// like any optional completion. Non-literal void initializers (a
|
|
@@ -3333,6 +3444,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3333
3444
|
// subtyping (`const p: {a: number} = wider;`) is rejected, not coerced.
|
|
3334
3445
|
init = lowerer.coerceInto(decl.initializer, init, type);
|
|
3335
3446
|
const local = lowerer.declareLocal(decl.name, decl.name.text, type, isLet);
|
|
3447
|
+
if (runtimeStringArithmetic && isStringArithmeticUnion(init.type)) {
|
|
3448
|
+
lowerer.runtimeOptionalArithmeticLocals.add(lowerer.runtimeOptionalRootOf(local));
|
|
3449
|
+
}
|
|
3336
3450
|
if (runtimeOptional) {
|
|
3337
3451
|
const runtimeOptionalRoot = lowerer.runtimeOptionalRootOf(local);
|
|
3338
3452
|
lowerer.runtimeOptionalLocals.add(runtimeOptionalRoot);
|
|
@@ -3340,6 +3454,40 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3340
3454
|
}
|
|
3341
3455
|
return { kind: "varDecl", localId: local.id, init, loc: locOf(decl) };
|
|
3342
3456
|
}
|
|
3457
|
+
function lowerVariableInitializer(lowerer, node) {
|
|
3458
|
+
let value = node;
|
|
3459
|
+
while (ts.isParenthesizedExpression(value) ||
|
|
3460
|
+
ts.isAsExpression(value) ||
|
|
3461
|
+
ts.isTypeAssertion(value) ||
|
|
3462
|
+
ts.isSatisfiesExpression(value)) {
|
|
3463
|
+
value = value.expression;
|
|
3464
|
+
}
|
|
3465
|
+
if (!ts.isObjectLiteralExpression(value))
|
|
3466
|
+
return lowerer.lowerExpr(node);
|
|
3467
|
+
const previous = new Map();
|
|
3468
|
+
for (const property of value.properties) {
|
|
3469
|
+
if (!ts.isPropertyAssignment(property) || !ts.isComputedPropertyName(property.name))
|
|
3470
|
+
continue;
|
|
3471
|
+
const keyNode = property.name.expression;
|
|
3472
|
+
const optional = lowerer.runtimeOptionalIdentifierValue(keyNode)?.value ??
|
|
3473
|
+
lowerAbsenceProbe(lowerer, keyNode);
|
|
3474
|
+
if (!optional || lowerer.runtimeOptionalWidening(optional.type, STRING) === null)
|
|
3475
|
+
continue;
|
|
3476
|
+
previous.set(keyNode, lowerer.chainRecvByNode.get(keyNode));
|
|
3477
|
+
lowerer.chainRecvByNode.set(keyNode, lowerer.ensureString(optional, keyNode));
|
|
3478
|
+
}
|
|
3479
|
+
try {
|
|
3480
|
+
return lowerer.lowerExpr(node);
|
|
3481
|
+
}
|
|
3482
|
+
finally {
|
|
3483
|
+
for (const [keyNode, prior] of previous) {
|
|
3484
|
+
if (prior)
|
|
3485
|
+
lowerer.chainRecvByNode.set(keyNode, prior);
|
|
3486
|
+
else
|
|
3487
|
+
lowerer.chainRecvByNode.delete(keyNode);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3343
3491
|
function immediatelyGuardedAbsenceProbe(lowerer, decl) {
|
|
3344
3492
|
if (decl.type || !ts.isIdentifier(decl.name) || !decl.initializer)
|
|
3345
3493
|
return false;
|
|
@@ -3432,8 +3580,8 @@ export function lowerSwitch(lowerer, stmt) {
|
|
|
3432
3580
|
* exactly `disc === test`), because the backend switch compares plain
|
|
3433
3581
|
* primitives only. The desugar is JS-exact for the shape it accepts and
|
|
3434
3582
|
* fences everything it cannot reproduce:
|
|
3435
|
-
* - the discriminant
|
|
3436
|
-
*
|
|
3583
|
+
* - the discriminant evaluates exactly once; effectful values stabilize
|
|
3584
|
+
* in a hidden local that every generated equality test reuses;
|
|
3437
3585
|
* - tests evaluate lazily in source order (the chain's short-circuit IS
|
|
3438
3586
|
* the switch's test order — grouped empty cases `case a: case b:` OR
|
|
3439
3587
|
* their tests, still in order);
|
|
@@ -3452,8 +3600,15 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3452
3600
|
if (disc.type.kind !== "union")
|
|
3453
3601
|
throw new InternalCompilerError("lowerer bug: non-union disc");
|
|
3454
3602
|
const unionType = disc.type;
|
|
3455
|
-
if (!
|
|
3456
|
-
lowerer.unsupported("SC1090", stmt.expression, "switch on
|
|
3603
|
+
if (!lowerer.eqComparableUnion(unionType.unionId)) {
|
|
3604
|
+
lowerer.unsupported("SC1090", stmt.expression, "switch on a union whose arms do not have static equality");
|
|
3605
|
+
}
|
|
3606
|
+
const prefix = [];
|
|
3607
|
+
let stableDisc = disc;
|
|
3608
|
+
if (!pureReemittable(disc)) {
|
|
3609
|
+
const temp = lowerer.declareHiddenLocal("%switch", unionType);
|
|
3610
|
+
prefix.push({ kind: "varDecl", localId: temp.id, init: disc, loc });
|
|
3611
|
+
stableDisc = { kind: "varRef", localId: temp.id, type: unionType, loc };
|
|
3457
3612
|
}
|
|
3458
3613
|
const clauses = stmt.caseBlock.clauses;
|
|
3459
3614
|
// An unlabeled break at a clause's END exits the switch — the chain's
|
|
@@ -3480,13 +3635,19 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3480
3635
|
}
|
|
3481
3636
|
}
|
|
3482
3637
|
}
|
|
3638
|
+
const statementExits = (last) => {
|
|
3639
|
+
if (!last)
|
|
3640
|
+
return false;
|
|
3641
|
+
if ((ts.isBreakStatement(last) && !last.label) ||
|
|
3642
|
+
ts.isReturnStatement(last) ||
|
|
3643
|
+
ts.isThrowStatement(last) ||
|
|
3644
|
+
ts.isContinueStatement(last))
|
|
3645
|
+
return true;
|
|
3646
|
+
return ts.isBlock(last) && statementExits(last.statements[last.statements.length - 1]);
|
|
3647
|
+
};
|
|
3483
3648
|
const exits = (clause) => {
|
|
3484
3649
|
const last = clause.statements[clause.statements.length - 1];
|
|
3485
|
-
return (
|
|
3486
|
-
((ts.isBreakStatement(last) && !last.label) ||
|
|
3487
|
-
ts.isReturnStatement(last) ||
|
|
3488
|
-
ts.isThrowStatement(last) ||
|
|
3489
|
-
ts.isContinueStatement(last)));
|
|
3650
|
+
return statementExits(last);
|
|
3490
3651
|
};
|
|
3491
3652
|
// The whole case-body sequence is ONE lexical scope, like the real
|
|
3492
3653
|
// switch lowering.
|
|
@@ -3515,14 +3676,14 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3515
3676
|
// never matches; coercing the literal into the union would hit
|
|
3516
3677
|
// the stranded-arm trap and throw where JS just skips the case).
|
|
3517
3678
|
const unitTest = test.kind === "unitLit"
|
|
3518
|
-
? lowerer.lowerUnitComparison(
|
|
3679
|
+
? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(clause.expression))
|
|
3519
3680
|
: null;
|
|
3520
3681
|
pendingTests.push(unitTest ?? {
|
|
3521
3682
|
kind: "unionEq",
|
|
3522
3683
|
unionId: unionType.unionId,
|
|
3523
3684
|
negated: false,
|
|
3524
3685
|
sameValue: false,
|
|
3525
|
-
left:
|
|
3686
|
+
left: stableDisc,
|
|
3526
3687
|
right: lowerer.coerceInto(clause.expression, test, unionType),
|
|
3527
3688
|
type: BOOL,
|
|
3528
3689
|
loc: locOf(clause.expression),
|
|
@@ -3563,7 +3724,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3563
3724
|
}
|
|
3564
3725
|
chain = [{ kind: "if", cond, then: g.body, else_: chain.length > 0 ? chain : null, loc }];
|
|
3565
3726
|
}
|
|
3566
|
-
return { kind: "block", body: chain, loc };
|
|
3727
|
+
return { kind: "block", body: [...prefix, ...chain], loc };
|
|
3567
3728
|
}
|
|
3568
3729
|
finally {
|
|
3569
3730
|
lowerer.scopes.pop();
|
|
@@ -3650,7 +3811,10 @@ function lowerDeleteStatement(lowerer, expr) {
|
|
|
3650
3811
|
return { kind: "strLit", value: target.name.text, type: STRING, loc: locOf(target.name) };
|
|
3651
3812
|
}
|
|
3652
3813
|
const keyNode = target.argumentExpression;
|
|
3653
|
-
|
|
3814
|
+
let key = lowerer.lowerExpr(keyNode);
|
|
3815
|
+
if (lowerer.runtimeOptionalWidening(key.type, STRING) !== null) {
|
|
3816
|
+
key = lowerer.ensureString(key, keyNode);
|
|
3817
|
+
}
|
|
3654
3818
|
if (key.type.kind !== "string") {
|
|
3655
3819
|
lowerer.unsupported("SC1090", keyNode, `'delete' with '${lowerer.fmt(key.type)}' keys (index-signature and env keys are strings)`);
|
|
3656
3820
|
}
|
|
@@ -4135,6 +4299,21 @@ export function lowerExprStatement(lowerer, expr) {
|
|
|
4135
4299
|
if (ts.isElementAccessExpression(expr.left))
|
|
4136
4300
|
return lowerer.lowerElementWrite(expr);
|
|
4137
4301
|
if (ts.isPropertyAccessExpression(expr.left)) {
|
|
4302
|
+
// Native arrays have a writable length property. The runtime grows
|
|
4303
|
+
// by creating holes and truncates with RC-aware removal; keep this
|
|
4304
|
+
// as a statement operation so invalid lengths can use the normal
|
|
4305
|
+
// pending-exception path.
|
|
4306
|
+
if (!expr.left.questionDotToken && expr.left.name.text === "length") {
|
|
4307
|
+
const arrayType = lowerer.mapTypeOf(lowerer.typeOf(expr.left.expression));
|
|
4308
|
+
if (arrayType?.kind === "array") {
|
|
4309
|
+
const arr = lowerer.lowerExpr(expr.left.expression);
|
|
4310
|
+
if (arr.type.kind !== "array") {
|
|
4311
|
+
lowerer.unsupported("SC1090", expr.left.expression, "writing the length of an array-typed value that remains an island handle");
|
|
4312
|
+
}
|
|
4313
|
+
const length = lowerer.lowerExprExpecting(expr.right, F64);
|
|
4314
|
+
return { kind: "arraySetLength", arr, length, loc: locOf(expr) };
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4138
4317
|
// `process.env.NAME = v` — setenv(3): later env reads and spawned
|
|
4139
4318
|
// children observe the write, exactly Node. Values are strings
|
|
4140
4319
|
// (Node stringifies everything; a non-string RHS fences instead
|
|
@@ -4571,6 +4750,7 @@ function lowerCompoundToTarget(lowerer, expr, compound, target) {
|
|
|
4571
4750
|
const loc = locOf(expr);
|
|
4572
4751
|
const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
|
|
4573
4752
|
const rhs = lowerer.lowerExpr(expr.right);
|
|
4753
|
+
const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
|
|
4574
4754
|
let value;
|
|
4575
4755
|
if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
|
|
4576
4756
|
const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
|
|
@@ -4587,8 +4767,8 @@ function lowerCompoundToTarget(lowerer, expr, compound, target) {
|
|
|
4587
4767
|
else if (compound === "+" && target.type.kind === "string") {
|
|
4588
4768
|
value = { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
|
|
4589
4769
|
}
|
|
4590
|
-
else if (target.type.kind === "f64" &&
|
|
4591
|
-
value = { kind: "bin", op: compound, left: read, right:
|
|
4770
|
+
else if (target.type.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
4771
|
+
value = { kind: "bin", op: compound, left: read, right: numericRhs, type: F64, loc };
|
|
4592
4772
|
}
|
|
4593
4773
|
else if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
|
|
4594
4774
|
(target.type.kind === "dyn" || target.type.kind === "f64") &&
|
|
@@ -4843,6 +5023,16 @@ export function lowerDestructuringAssignParts(lowerer, target, rhs, loc) {
|
|
|
4843
5023
|
* source-spelling TypeError machinery cannot apply), `blame` the node
|
|
4844
5024
|
* fences point at. */
|
|
4845
5025
|
function destructuringAssignInto(lowerer, target, init, rhs, blame, loc) {
|
|
5026
|
+
if (init.type.kind === "union") {
|
|
5027
|
+
const source = lowerer.declareHiddenLocal("%destrSource", init.type);
|
|
5028
|
+
const out = [{ kind: "varDecl", localId: source.id, init, loc }];
|
|
5029
|
+
const value = varRef(source.id, init.type, loc);
|
|
5030
|
+
const present = patternSourceValue(lowerer, value, ts.isArrayLiteralExpression(target), out, loc);
|
|
5031
|
+
if (!typeEquals(present.type, init.type)) {
|
|
5032
|
+
const sub = destructuringAssignInto(lowerer, target, present, rhs, blame, loc);
|
|
5033
|
+
return { stmts: [...out, ...sub.stmts], value };
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
4846
5036
|
// IR-string sources in JS files split two ways the carrier type
|
|
4847
5037
|
// cannot: a STDLIB GLOBAL (`({ subtle } = globalThis.crypto)`) — the
|
|
4848
5038
|
// declaration path binds member identity tokens into FRESH bindings,
|
|
@@ -5316,14 +5506,10 @@ function lowerAssignTargetInto(lowerer, out, targetNode, blame, dflt, valueOf) {
|
|
|
5316
5506
|
}
|
|
5317
5507
|
const idx = lowerer.declareHiddenLocal("%dtIdx", F64);
|
|
5318
5508
|
out.push({ kind: "varDecl", localId: idx.id, init: index, loc });
|
|
5319
|
-
const value =
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
index: { kind: "varRef", localId: idx.id, type: F64, loc },
|
|
5324
|
-
value,
|
|
5325
|
-
loc,
|
|
5326
|
-
});
|
|
5509
|
+
const value = valueOf(recvT.elem);
|
|
5510
|
+
const stored = lowerer.declareHiddenLocal("%dtValue", value.type);
|
|
5511
|
+
out.push({ kind: "varDecl", localId: stored.id, init: value, loc });
|
|
5512
|
+
out.push(arrayValueStore(lowerer, varRef(recv.id, arr.type, loc), varRef(idx.id, F64, loc), varRef(stored.id, value.type, loc), recvT.elem, loc));
|
|
5327
5513
|
return;
|
|
5328
5514
|
}
|
|
5329
5515
|
}
|
|
@@ -5499,11 +5685,9 @@ function lowerArrayAssignInto(lowerer, out, target, tmpRef, blame, loc) {
|
|
|
5499
5685
|
loc: locOf(at),
|
|
5500
5686
|
};
|
|
5501
5687
|
}
|
|
5502
|
-
//
|
|
5503
|
-
// array traps (the arrayGet discipline, divergence 4's policy; JS
|
|
5504
|
-
// would answer undefined).
|
|
5688
|
+
// Assignment patterns consume the same values as binding patterns.
|
|
5505
5689
|
const elemType = srcType.elem;
|
|
5506
|
-
return
|
|
5690
|
+
return arrayValueRead(lowerer, tmpRef(), numLit(i, locOf(at)), elemType, locOf(at));
|
|
5507
5691
|
};
|
|
5508
5692
|
elements.forEach((el, i) => {
|
|
5509
5693
|
if (ts.isOmittedExpression(el))
|
|
@@ -5556,7 +5740,7 @@ function lowerArrayAssignInto(lowerer, out, target, tmpRef, blame, loc) {
|
|
|
5556
5740
|
}
|
|
5557
5741
|
return isTuple
|
|
5558
5742
|
? tupleTailValue(lowerer, el, tmpRef, srcType, shape, i, targetT)
|
|
5559
|
-
:
|
|
5743
|
+
: arrayRestValue(tmpRef(), i, srcType, locOf(el));
|
|
5560
5744
|
}
|
|
5561
5745
|
if (isTuple && defaultNode !== null && targetT !== null && !shape.fields.some((f) => f.name === String(i))) {
|
|
5562
5746
|
// Past the tuple's end the read is always undefined — the default
|
|
@@ -5777,6 +5961,19 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5777
5961
|
}
|
|
5778
5962
|
iterSrc ??= stmt.expression;
|
|
5779
5963
|
let iterable = lowerer.lowerExpr(iterSrc);
|
|
5964
|
+
// A nested loop receiver can be an OOB-safe array read from the outer
|
|
5965
|
+
// loop (`for (const row of grid) for (const cell of row)`). The checker
|
|
5966
|
+
// sees the inner value as an array, while its runtime slot is array |
|
|
5967
|
+
// undefined; validate the receiver before selecting the array iterator.
|
|
5968
|
+
const checkerIterable = lowerer.mapTypeOf(lowerer.typeOf(iterSrc));
|
|
5969
|
+
if (iterable.type.kind === "union" && lowerer.armTag(iterable.type.unionId, UNDEFINED_T) >= 0 && checkerIterable?.kind === "array") {
|
|
5970
|
+
const present = lowerer.stripUndefinedArm(iterable.type);
|
|
5971
|
+
const helper = present.kind === "array"
|
|
5972
|
+
? lowerer.narrowedArmHelper(iterable.type.unionId, present, locOf(iterSrc))
|
|
5973
|
+
: null;
|
|
5974
|
+
if (helper)
|
|
5975
|
+
iterable = { kind: "call", callee: helper, args: [iterable], type: present, loc: locOf(iterSrc) };
|
|
5976
|
+
}
|
|
5780
5977
|
if (iterable.type.kind === "bytes") {
|
|
5781
5978
|
return lowerForOfBytes(lowerer, stmt, iterable, labels);
|
|
5782
5979
|
}
|
|
@@ -5902,6 +6099,29 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5902
6099
|
lowerer.badType(stmt.expression, lowerer.typeOf(stmt.expression));
|
|
5903
6100
|
}
|
|
5904
6101
|
}
|
|
6102
|
+
const elemValueT = arrayValueType(lowerer, iterable.type.elem);
|
|
6103
|
+
// Native arrays retain their payload ABI; the loop binds the value yielded
|
|
6104
|
+
// by Get, which is undefined for both holes and present undefined slots.
|
|
6105
|
+
const sourceT = iterable.type;
|
|
6106
|
+
const source = lowerer.declareHiddenLocal("%arrayIterator", sourceT);
|
|
6107
|
+
const cursor = lowerer.declareHiddenLocal("%arrayCursor", F64);
|
|
6108
|
+
cursor.mutable = true;
|
|
6109
|
+
const forValues = (localId, body) => ({
|
|
6110
|
+
kind: "block",
|
|
6111
|
+
body: [
|
|
6112
|
+
{ kind: "varDecl", localId: source.id, init: iterable, loc: locOf(stmt) },
|
|
6113
|
+
{
|
|
6114
|
+
kind: "for",
|
|
6115
|
+
init: { kind: "varDecl", localId: cursor.id, init: numLit(0, locOf(stmt)), loc: locOf(stmt) },
|
|
6116
|
+
cond: { kind: "bin", op: "<", left: varRef(cursor.id, F64, locOf(stmt)), right: { kind: "arrIntrinsic", method: "length", receiver: varRef(source.id, sourceT, locOf(stmt)), args: [], type: F64, loc: locOf(stmt) }, type: BOOL, loc: locOf(stmt) },
|
|
6117
|
+
update: { kind: "assign", localId: cursor.id, value: { kind: "bin", op: "+", left: varRef(cursor.id, F64, locOf(stmt)), right: numLit(1, locOf(stmt)), type: F64, loc: locOf(stmt) }, loc: locOf(stmt) },
|
|
6118
|
+
body: [{ kind: "varDecl", localId, init: arrayValueRead(lowerer, varRef(source.id, sourceT, locOf(stmt)), varRef(cursor.id, F64, locOf(stmt)), sourceT.elem, locOf(stmt)), loc: locOf(stmt) }, ...body],
|
|
6119
|
+
...(labels && { labels }),
|
|
6120
|
+
loc: locOf(stmt),
|
|
6121
|
+
},
|
|
6122
|
+
],
|
|
6123
|
+
loc: locOf(stmt),
|
|
6124
|
+
});
|
|
5905
6125
|
if (!ts.isVariableDeclarationList(stmt.initializer)) {
|
|
5906
6126
|
// `for (x of xs)` over a PRE-DECLARED writable binding: JS assigns
|
|
5907
6127
|
// the existing binding once per pass — one shared binding across
|
|
@@ -5914,18 +6134,18 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5914
6134
|
while (ts.isParenthesizedExpression(target))
|
|
5915
6135
|
target = target.expression;
|
|
5916
6136
|
if (ts.isArrayLiteralExpression(target) || ts.isObjectLiteralExpression(target)) {
|
|
5917
|
-
const elemType =
|
|
6137
|
+
const elemType = elemValueT;
|
|
5918
6138
|
const loc = locOf(target);
|
|
5919
6139
|
const tmp = lowerer.declareHiddenLocal("%vof", elemType);
|
|
5920
6140
|
const elemRef = { kind: "varRef", localId: tmp.id, type: elemType, loc };
|
|
5921
6141
|
const assigns = lowerDestructuringAssign(lowerer, target, elemRef, target, loc);
|
|
5922
6142
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
5923
|
-
return
|
|
6143
|
+
return forValues(tmp.id, [assigns, ...body]);
|
|
5924
6144
|
}
|
|
5925
6145
|
if (ts.isIdentifier(target)) {
|
|
5926
6146
|
const writable = lowerer.resolveWritable(target);
|
|
5927
6147
|
if (writable) {
|
|
5928
|
-
const elemType =
|
|
6148
|
+
const elemType = elemValueT;
|
|
5929
6149
|
const loc = locOf(target);
|
|
5930
6150
|
const tmp = lowerer.declareHiddenLocal("%vof", elemType);
|
|
5931
6151
|
const write = {
|
|
@@ -5935,7 +6155,7 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5935
6155
|
loc,
|
|
5936
6156
|
};
|
|
5937
6157
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
5938
|
-
return
|
|
6158
|
+
return forValues(tmp.id, [write, ...body]);
|
|
5939
6159
|
}
|
|
5940
6160
|
}
|
|
5941
6161
|
lowerer.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
|
|
@@ -5955,13 +6175,13 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5955
6175
|
if (ts.isArrayBindingPattern(decl.name) || ts.isObjectBindingPattern(decl.name)) {
|
|
5956
6176
|
lowerer.scopes.push(new Map());
|
|
5957
6177
|
try {
|
|
5958
|
-
const elemType =
|
|
6178
|
+
const elemType = elemValueT;
|
|
5959
6179
|
const loc = locOf(decl.name);
|
|
5960
6180
|
const tmp = lowerer.declareHiddenLocal("%destr", elemType);
|
|
5961
6181
|
const binds = [];
|
|
5962
6182
|
lowerer.lowerBindingPattern(decl.name, () => ({ kind: "varRef", localId: tmp.id, type: elemType, loc }), elemType, isLet, binds);
|
|
5963
6183
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
5964
|
-
return
|
|
6184
|
+
return forValues(tmp.id, [...binds, ...body]);
|
|
5965
6185
|
}
|
|
5966
6186
|
finally {
|
|
5967
6187
|
lowerer.scopes.pop();
|
|
@@ -5977,7 +6197,7 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5977
6197
|
// after the loop (both Node-exact for var).
|
|
5978
6198
|
const varTarget = forOfVarTarget(lowerer, decl);
|
|
5979
6199
|
if (varTarget) {
|
|
5980
|
-
const elemType =
|
|
6200
|
+
const elemType = elemValueT;
|
|
5981
6201
|
const loc = locOf(decl.name);
|
|
5982
6202
|
const tmp = lowerer.declareHiddenLocal("%vof", elemType);
|
|
5983
6203
|
const write = {
|
|
@@ -5987,11 +6207,16 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5987
6207
|
loc,
|
|
5988
6208
|
};
|
|
5989
6209
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
5990
|
-
return
|
|
6210
|
+
return forValues(tmp.id, [write, ...body]);
|
|
6211
|
+
}
|
|
6212
|
+
const local = lowerer.declareLocal(decl.name, decl.name.text, elemValueT, isLet);
|
|
6213
|
+
if (!typeEquals(elemValueT, sourceT.elem)) {
|
|
6214
|
+
const root = lowerer.runtimeOptionalRootOf(local);
|
|
6215
|
+
lowerer.runtimeOptionalLocals.add(root);
|
|
6216
|
+
lowerer.runtimeOptionalStorageLocals.add(root);
|
|
5991
6217
|
}
|
|
5992
|
-
const local = lowerer.declareLocal(decl.name, decl.name.text, iterable.type.elem, isLet);
|
|
5993
6218
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
5994
|
-
return
|
|
6219
|
+
return forValues(local.id, body);
|
|
5995
6220
|
}
|
|
5996
6221
|
finally {
|
|
5997
6222
|
lowerer.scopes.pop();
|
|
@@ -6087,8 +6312,9 @@ function lowerForOfStoredNumericIterator(lowerer, stmt, state, labels) {
|
|
|
6087
6312
|
type: F64,
|
|
6088
6313
|
loc,
|
|
6089
6314
|
};
|
|
6315
|
+
const valueT = sourceT.kind === "array" ? arrayValueType(lowerer, F64) : F64;
|
|
6090
6316
|
const sourceValue = () => sourceT.kind === "array"
|
|
6091
|
-
?
|
|
6317
|
+
? arrayValueRead(lowerer, sourceRef(), indexRef(), F64, loc)
|
|
6092
6318
|
: {
|
|
6093
6319
|
kind: "bytesIntrinsic",
|
|
6094
6320
|
method: "get",
|
|
@@ -6102,9 +6328,14 @@ function lowerForOfStoredNumericIterator(lowerer, stmt, state, labels) {
|
|
|
6102
6328
|
try {
|
|
6103
6329
|
const varTarget = exprTarget ?? (decl ? forOfVarTarget(lowerer, decl) : null);
|
|
6104
6330
|
const value = varTarget
|
|
6105
|
-
? lowerer.declareHiddenLocal("%numiterValue",
|
|
6106
|
-
: lowerer.declareLocal(decl.name, decl.name.text,
|
|
6107
|
-
|
|
6331
|
+
? lowerer.declareHiddenLocal("%numiterValue", valueT)
|
|
6332
|
+
: lowerer.declareLocal(decl.name, decl.name.text, valueT, isLet);
|
|
6333
|
+
if (!varTarget && valueT.kind === "union") {
|
|
6334
|
+
const root = lowerer.runtimeOptionalRootOf(value);
|
|
6335
|
+
lowerer.runtimeOptionalLocals.add(root);
|
|
6336
|
+
lowerer.runtimeOptionalStorageLocals.add(root);
|
|
6337
|
+
}
|
|
6338
|
+
const valueRef = varRef(value.id, valueT, loc);
|
|
6108
6339
|
const blame = decl?.name ?? exprTargetNode ?? stmt.initializer;
|
|
6109
6340
|
const head = [
|
|
6110
6341
|
{
|