mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-05 08:13:06 +08:00
Leave counters unbounded when a nested scan is not walked pass by pass
highestCount walks a nested while loop from its bound only when the loop is a statement of the body or of its blocks and branches. A nested scan inside a labeled block or an expression reached the generic branch, which counted its increments once although the loop can repeat them. Such a loop now leaves the counter without a range.
This commit is contained in:
@@ -395,11 +395,15 @@ export function guardedLoopCounterFacts(
|
||||
|
||||
/** The largest value counter `id` takes in one pass of `body`, which starts
|
||||
* at most at `entry`. The caller has checked that the body changes the
|
||||
* counter only by positive literal increments outside nested loops, switch,
|
||||
* exception and labeled regions, so those regions leave it unchanged, and
|
||||
* every exit from a straight-line path leaves the body. An `if` whose
|
||||
* condition does not write the counter caps it in the then branch by the
|
||||
* condition's bounds; any other statement adds the increments it holds. */
|
||||
* counter only by positive literal increments outside switch, exception and
|
||||
* labeled loop regions and loops other than nested while loops whose
|
||||
* conditions bound it, and every exit from a straight-line path leaves the
|
||||
* body. An `if` whose condition does not write the counter caps it in the
|
||||
* then branch by the condition's bounds. A nested while loop that is a
|
||||
* statement of the body, or of its blocks and branches, is walked from its
|
||||
* bound; any other statement adds the increments it holds, and one that
|
||||
* holds a loop writing the counter (in an expression or a labeled block)
|
||||
* leaves it unbounded. */
|
||||
function highestCount(
|
||||
body: readonly IrStmt[],
|
||||
id: string,
|
||||
@@ -416,6 +420,13 @@ function highestCount(
|
||||
};
|
||||
const stmt = (x: IrStmt): boolean => {
|
||||
if (x.kind === "assign" && x.localId === id) total += increment(id, x.value) ?? 0;
|
||||
// A loop reached here, inside an expression or a labeled region rather
|
||||
// than through `walk`, can repeat its increments: no bound.
|
||||
if (
|
||||
(x.kind === "while" || x.kind === "doWhile" || x.kind === "for" || x.kind === "forOf") &&
|
||||
writtenLocals([x]).has(id)
|
||||
)
|
||||
total = Infinity;
|
||||
return everyStmtChild(x, expr, stmt);
|
||||
};
|
||||
if (expression) expr(e as IrExpr);
|
||||
|
||||
@@ -863,6 +863,15 @@ test("a nested while loop may advance a counter its own condition bounds", () =>
|
||||
),
|
||||
);
|
||||
expect(run(writing).after).toBeNull();
|
||||
// The same scan inside a labeled block or an expression is not walked
|
||||
// pass by pass; its increments could repeat, so the counter stays unbounded.
|
||||
expect(run({ loc, kind: "block", labels: ["scan"], body: [bounded] }).after).toBeNull();
|
||||
const inExpression: IrStmt = {
|
||||
loc,
|
||||
kind: "exprStmt",
|
||||
expr: { loc, kind: "seqExpr", stmts: [bounded], result: num(0), type: F64 },
|
||||
};
|
||||
expect(run(inExpression).after).toBeNull();
|
||||
});
|
||||
|
||||
test("an array length bounds while and for counters even when the body resizes the array", () => {
|
||||
|
||||
Reference in New Issue
Block a user