Skip to content

Commit

Permalink
Ensure shared flow nodes are cached
Browse files Browse the repository at this point in the history
Flow nodes that optionally provide a flow type will delegate to their antecedent
to determine a flow type using a loop in `getFlowTypeOfReference`. If these nodes
are marked as shared then their result should be cached for future lookups, but
the registration inside `getFlowTypeOfReference` did only consider the most-recently
processed flow node. Any flow nodes up to that point that did not provide a type
but were marked as shared would therefore not be cached at all.

This commit keeps track of the first seen shared flow node and ensures that the
result type is cached for that node.
  • Loading branch information
JoostK committed Nov 5, 2020
1 parent 76cf8fd commit 3cc2032
Show file tree
Hide file tree
Showing 5 changed files with 962 additions and 5 deletions.
27 changes: 22 additions & 5 deletions src/compiler/checker.ts
Expand Up @@ -21673,6 +21673,12 @@ namespace ts {
}
}

function addSharedFlowType(flow: FlowNode, type: FlowType): void {
sharedFlowNodes[sharedFlowCount] = flow;
sharedFlowTypes[sharedFlowCount] = type;
sharedFlowCount++;
}

function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
let key: string | undefined;
let isKeySet = false;
Expand Down Expand Up @@ -21715,6 +21721,7 @@ namespace ts {
return errorType;
}
flowDepth++;
let sharedFlow: FlowNode | undefined;
while (true) {
const flags = flow.flags;
if (flags & FlowFlags.Shared) {
Expand All @@ -21727,6 +21734,12 @@ namespace ts {
return sharedFlowTypes[i];
}
}

// Keep track of the shared flow that is seen first to ensure that the
// computed type is saved in the shared flow type cache.
if (!sharedFlow) {
sharedFlow = flow;
}
}
let type: FlowType | undefined;
if (flags & FlowFlags.Assignment) {
Expand Down Expand Up @@ -21790,11 +21803,15 @@ namespace ts {
// simply return the non-auto declared type to reduce follow-on errors.
type = convertAutoToAny(declaredType);
}
if (flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
sharedFlowNodes[sharedFlowCount] = flow;
sharedFlowTypes[sharedFlowCount] = type;
sharedFlowCount++;
if (sharedFlow) {
// If the type for a shared flow was computed, record the associated type in the cache.
addSharedFlowType(sharedFlow, type);

// If a shared flow was iterated over and the current flow is also shared,
// then also record the associated type with the current flow.
if (flow !== sharedFlow && flags & FlowFlags.Shared) {
addSharedFlowType(flow, type);
}
}
flowDepth--;
return type;
Expand Down
@@ -0,0 +1,125 @@
//// [controlFlowManyCallExpressionStatementsPerf.ts]
function test(x: boolean): boolean { return x; }

let state = true;

if (state) {
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
test(state as any && state);
}

//// [controlFlowManyCallExpressionStatementsPerf.js]
function test(x) { return x; }
var state = true;
if (state) {
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
test(state && state);
}

0 comments on commit 3cc2032

Please sign in to comment.