/** * A loop's back edge: the second pass over a body, run with the facts the * first pass falsified already removed, so that what the loop's exit keeps is * true on every iteration and not only on the first. * * D114 R1d: `loopFlowContexts`, `loopReanalysisDepth`, `reanalyzeLoopBackEdge` * and the three helpers that pass exists for — the budget test, the duplicate * diagnostics a second pass would otherwise report twice, and the cached * per-span answers the second pass has to forget — move together. The * statement forms that push and pop a context stay on the analyzer's `for` and * `while` handling and reach the stack through `contexts`. */ import { type Statement } from "../../ast.ts"; import { type RuntimeNarrowingGuard } from "../../contracts.ts"; import { type Diagnostic } from "../../diagnostic.ts"; import { type Span } from "../../source.ts"; import { type ValueType } from "../../types.ts"; import { type VisibleScopeDepth } from "../scopes.ts"; import { type FlowFactInvalidations, type FlowFactsSnapshot } from "./facts.ts"; /** What a loop's back-edge pass answered: its context, or that the budget widened the exit. */ export interface LoopBackEdgeOutcome { readonly repeated: LoopFlowContext | null; /** True when the pass was skipped, so the loop's exit may keep no unconfirmed fact. */ readonly widened: boolean; } export interface LoopFlowContext { readonly baseline: FlowFactsSnapshot; /** The scopes visible outside the loop, so a break's facts can be stated in their terms. */ readonly visible: VisibleScopeDepth; readonly carried: FlowFactInvalidations[]; readonly backEdges: FlowFactInvalidations[]; /** FLW-N6: the facts holding at each `break`, one entry per break edge. */ readonly breakFacts: ReadonlyMap[]; sawBreak: boolean; } /** * Everything the loop half asks of the analyzer that hosts it, and nothing * more. The three caches a back-edge pass invalidates arrive as live maps: the * pass runs while they are being written. */ export interface LoopFlowHost { analyzeIsolatedFlow(snapshot: FlowFactsSnapshot, analyze: () => void): FlowFactInvalidations; readonly diagnostics: Diagnostic[]; flowSnapshotAfterInvalidations(baseline: FlowFactsSnapshot, invalidations: readonly FlowFactInvalidations[]): FlowFactsSnapshot; readonly inferredExpressionTypes: Map; readonly logicalConditionNarrowings: Map; readonly falsy: ReadonlyMap; }>; restoreFlowFacts(snapshot: FlowFactsSnapshot): void; readonly runtimeNarrowings: Map; } export declare class LoopFlow { private readonly host; /** The loops whose bodies are being analyzed, innermost last. */ readonly contexts: LoopFlowContext[]; /** How many loop back-edge passes are running; see `reanalyzeLoopBackEdge`. */ private reanalysisDepth; constructor(host: LoopFlowHost); /** * A loop's back-edge pass re-runs the whole body, and a nested loop inside * that pass runs its own, so the work doubled with every level of loop * nesting: fourteen levels of `while` in a 91-line file took 2.4 seconds and * seventeen took 35. The passes are budgeted by how many back-edge passes * are already running. Past the budget a loop analyzes its body once and its * exit keeps nothing — `widened` — which is what the loop would answer if * its back edge had falsified every fact, so the degradation only ever * removes a fact, never invents one. Real code does not nest loops four * deep, so nothing reachable by hand reaches the budget. */ reanalyzeLoopBackEdge(baseline: FlowFactsSnapshot, visible: VisibleScopeDepth, backEdges: readonly FlowFactInvalidations[], body: readonly Statement[], diagnosticStart: number, analyze: () => void): LoopBackEdgeOutcome; flowInvalidationsAffectFacts(invalidations: readonly FlowFactInvalidations[]): boolean; private deduplicateDiagnostics; clearCachedFlowTypes(statements: readonly Statement[]): void; clearCachedFlowTypesInSpan(sourceSpan: Span): void; } //# sourceMappingURL=loops.d.ts.map