mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
Check nullable destructuring sources through their unit arm
The TypeError check for a null or undefined source now reads the unit arm the value holds as a dyn null or undefined, instead of converting the whole union to dyn. A union whose data arm has no dyn form, such as a record holding a generator, compiles again instead of failing with SC9001.
This commit is contained in:
@@ -2581,14 +2581,46 @@ export function lowerDestructuringDecl(
|
||||
(arm.kind === "array" || arm.kind === "record")
|
||||
) {
|
||||
const tag = def.arms.indexOf(arm);
|
||||
const unionId = init.type.unionId;
|
||||
const utmp = lowerer.declareHiddenLocal("%destrU", init.type);
|
||||
out.push({ kind: "varDecl", localId: utmp.id, init, loc });
|
||||
const uref: IrExpr = { kind: "varRef", localId: utmp.id, type: init.type, loc };
|
||||
// The failure branch holds one of the unit arms: the check reads it
|
||||
// as the dyn null or undefined it is, so the data arm needs no dyn
|
||||
// form (a record holding a generator has none).
|
||||
const unitValue = (): IrExpr => {
|
||||
const unitOf = (t: IrType): IrExpr =>
|
||||
lowerer.coerceToExpected(
|
||||
{ kind: "unitLit", unit: t.kind === "nullT" ? "null" : "undefined", type: t, loc },
|
||||
DYN,
|
||||
);
|
||||
const units = def.arms.filter(isUnitType);
|
||||
const last = units[units.length - 1]!;
|
||||
return units.slice(0, -1).reduceRight<IrExpr>(
|
||||
(rest, t) => ({
|
||||
kind: "ternary",
|
||||
cond: {
|
||||
kind: "unionIsTag",
|
||||
unionId,
|
||||
tag: def.arms.indexOf(t),
|
||||
negated: false,
|
||||
value: uref,
|
||||
type: BOOL,
|
||||
loc,
|
||||
},
|
||||
then: unitOf(t),
|
||||
else_: rest,
|
||||
type: DYN,
|
||||
loc,
|
||||
}),
|
||||
unitOf(last),
|
||||
);
|
||||
};
|
||||
const spelling = destrSpellingOf(src);
|
||||
const check: IrExpr | null = ts.isArrayBindingPattern(decl.name)
|
||||
? {
|
||||
kind: "dynIterN",
|
||||
value: lowerer.coerceToExpected(uref, DYN),
|
||||
value: unitValue(),
|
||||
count: decl.name.elements.nodes.length,
|
||||
...(dynSpell ? { notIterableMessage: dynSpell } : {}),
|
||||
type: DYN,
|
||||
@@ -2597,7 +2629,7 @@ export function lowerDestructuringDecl(
|
||||
: spelling !== null
|
||||
? {
|
||||
kind: "dynDestrCheck",
|
||||
value: lowerer.coerceToExpected(uref, DYN),
|
||||
value: unitValue(),
|
||||
spelling,
|
||||
...firstPatternProp(lowerer, decl.name as ts.ObjectBindingPattern),
|
||||
type: DYN,
|
||||
|
||||
@@ -56,6 +56,36 @@ show("array-function-call", () => {
|
||||
console.log("never", first);
|
||||
});
|
||||
|
||||
// A source that may be null or undefined, and data arms holding a
|
||||
// generator, which has no untyped form.
|
||||
function* count() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
function pick(kind) {
|
||||
if (kind === 0) return null;
|
||||
if (kind === 1) return undefined;
|
||||
return { counter: count(), label: "picked" };
|
||||
}
|
||||
function counters(present) {
|
||||
return present ? [count(), count()] : null;
|
||||
}
|
||||
show("object-null-arm", () => {
|
||||
const { counter } = pick(0);
|
||||
console.log("never", counter);
|
||||
});
|
||||
show("object-undefined-arm", () => {
|
||||
const { label } = pick(1);
|
||||
console.log("never", label);
|
||||
});
|
||||
show("array-generator-elements", () => {
|
||||
const [first] = counters(false);
|
||||
console.log("never", first);
|
||||
});
|
||||
const { counter, label } = pick(2);
|
||||
const [left, right] = counters(true);
|
||||
console.log(label, [...counter].join(","), [...left, ...right].join(","));
|
||||
|
||||
// Sources that hold a value bind as before.
|
||||
const { index } = "abc".match(/b/);
|
||||
const [whole] = "abc".match(/c/);
|
||||
|
||||
Reference in New Issue
Block a user