diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ba06e08a301..c49ef71c19d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6,6 +6,7 @@ namespace ts { let nextNodeId = 1; let nextMergeId = 1; let nextFlowId = 1; + let nextCheckerId = 1; const enum IterationUse { AllowsSyncIterablesFlag = 1 << 0, @@ -298,6 +299,7 @@ namespace ts { let instantiationDepth = 0; let constraintDepth = 0; let currentNode: Node | undefined; + let checkerId: number; const emptySymbols = createSymbolTable(); const identityMapper: (type: Type) => Type = identity; @@ -842,7 +844,6 @@ namespace ts { const flowLoopTypes: Type[][] = []; const sharedFlowNodes: FlowNode[] = []; const sharedFlowTypes: FlowType[] = []; - const flowNodeReachable: (boolean | undefined)[] = []; const potentialThisCollisions: Node[] = []; const potentialNewTargetCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -16993,17 +16994,21 @@ namespace ts { } function isReachableFlowNode(flow: FlowNode) { - return isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false); + return isReachableFlowNodeWorker(flow, /*noCacheCheck*/ false); } function isReachableFlowNodeWorker(flow: FlowNode, noCacheCheck: boolean): boolean { while (true) { const flags = flow.flags; - if (flags & FlowFlags.Shared) { + if (flags & (FlowFlags.Shared | FlowFlags.Assignment | FlowFlags.Label)) { if (!noCacheCheck) { - const id = getFlowNodeId(flow); - const reachable = flowNodeReachable[id]; - return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ true)); + if (flow.checkerId === checkerId) { + return !!(flow.flags & FlowFlags.Reachable); + } + const reachable = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ true); + flow.checkerId = checkerId; + flow.flags = (flow.flags & ~FlowFlags.Reachable) | (reachable ? FlowFlags.Reachable : 0); + return reachable; } noCacheCheck = false; } @@ -32287,6 +32292,9 @@ namespace ts { } function initializeTypeChecker() { + checkerId = nextCheckerId; + nextCheckerId++; + // Bind all source files and propagate errors for (const file of host.getSourceFiles()) { bindSourceFile(file, compilerOptions); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f80126b64c5..b9931adb604 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2582,6 +2582,8 @@ namespace ts { AfterFinally = 1 << 13, // Injected edge that links post-finally flow with the rest of the graph /** @internal */ Cached = 1 << 14, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant + /** @internal */ + Reachable = 1 << 15, // Reachability as computed by isReachableFlowNode Label = BranchLabel | LoopLabel, Condition = TrueCondition | FalseCondition } @@ -2600,6 +2602,8 @@ namespace ts { export interface FlowNodeBase { flags: FlowFlags; id?: number; // Node id used by flow type cache in checker + /** @internal */ + checkerId?: number; // Checker id for FlowFlags.Reachable } export interface FlowLock {