mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
The species lowering for a callback method rebuilt its `%arr.<method>` call by shape — the result had to be a call taking the receiver as its first argument — so two forms the method lowering does produce fell to SC1090 whenever the program declared a class that extends Array: - a filter whose callback narrows: the filtered array comes back through the checked-narrowing wrapper, `call(%arr.narrow, [call(%arr.filter, …)])`, and the species path saw a call whose first argument is not the receiver; - a flatMap whose callback returns a union with an array arm, which lowers to `%arr.flatMap.<n>` — a helper boundedArrayHofHelper rejected because arrHofInfo, which holds the pipeline-fusable helpers, has no entry for it. The call is now found by walking the expression for the `%arr.` helper call over the receiver, whatever wraps it, and boundedArrayHofHelper accepts any `%arr.` helper (forEach excepted, it makes no array), so the union flatMap earns a bounded variant. Hidden locals are still declared in the old order, so a program that compiled before gets byte-identical IR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
33 lines
1.5 KiB
TypeScript
33 lines
1.5 KiB
TypeScript
// One class that extends Array sends every array method call in the program
|
|
// through the species lowering, plain-array receivers included. Two callback
|
|
// forms the species path used to refuse: a filter whose callback narrows the
|
|
// element union (a type predicate, or a discriminant test tsc infers one
|
|
// from), which wraps the filtered array in a checked-narrowing conversion,
|
|
// and a flatMap whose callback returns a union holding an array arm.
|
|
class Stack extends Array<number> {}
|
|
|
|
type Item = { kind: "a"; n: number } | { kind: "b"; s: string };
|
|
const items: Item[] = [
|
|
{ kind: "a", n: 1 },
|
|
{ kind: "b", s: "x" },
|
|
{ kind: "a", n: 2 },
|
|
];
|
|
const isA = (item: Item): item is { kind: "a"; n: number } => item.kind === "a";
|
|
|
|
const tagged = items.filter(isA);
|
|
const inferred = items.filter((item) => item.kind === "b");
|
|
const blocked = items.filter((item) => {
|
|
return item.kind === "b";
|
|
});
|
|
const flattened = items.flatMap((item) => (item.kind === "a" ? [item.n] : []));
|
|
const mixed = items.flatMap((item) => (item.kind === "a" ? [item.n] : undefined));
|
|
console.log(tagged.length, tagged.map((item) => item.n).join(","));
|
|
console.log(inferred.length, inferred.map((item) => item.s).join(","), blocked.length);
|
|
console.log(flattened.join(","), mixed.length, mixed.join(","));
|
|
|
|
const stack = Stack.from([1, 2, 3]);
|
|
const even = stack.filter((n) => n % 2 === 0);
|
|
const doubled = stack.flatMap((n) => (n > 1 ? [n, n * 10] : []));
|
|
console.log(even instanceof Stack, even.join(","), stack.length);
|
|
console.log(doubled instanceof Stack, doubled.join(","), stack.length);
|