More efficient scheme for caching flow node reachability

This commit is contained in:
Anders Hejlsberg
2019-09-16 13:04:22 -07:00
parent 05d1e68e62
commit e97ebb7f1c
2 changed files with 18 additions and 6 deletions

View File

@@ -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);

View File

@@ -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 {