Skip to content

Commit ab0c1d6

Browse files
committed
Added performance optimization for code flow analysis within loops.
1 parent 6234fcd commit ab0c1d6

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed

packages/pyright-internal/src/analyzer/binder.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import {
9090
FlowExhaustedMatch,
9191
FlowFlags,
9292
FlowLabel,
93+
FlowLoopLabel,
9394
FlowNarrowForPattern,
9495
FlowNode,
9596
FlowPostContextManagerLabel,
@@ -2314,10 +2315,11 @@ export class Binder extends ParseTreeWalker {
23142315
}
23152316

23162317
private _createLoopLabel() {
2317-
const flowNode: FlowLabel = {
2318+
const flowNode: FlowLoopLabel = {
23182319
flags: FlowFlags.LoopLabel,
23192320
id: getUniqueFlowNodeId(),
23202321
antecedents: [],
2322+
expressions: undefined,
23212323
};
23222324
return flowNode;
23232325
}
@@ -2889,14 +2891,25 @@ export class Binder extends ParseTreeWalker {
28892891
}
28902892
}
28912893

2892-
private _bindLoopStatement(preLoopLabel: FlowLabel, postLoopLabel: FlowLabel, callback: () => void) {
2894+
private _bindLoopStatement(preLoopLabel: FlowLoopLabel, postLoopLabel: FlowLabel, callback: () => void) {
28932895
const savedContinueTarget = this._currentContinueTarget;
28942896
const savedBreakTarget = this._currentBreakTarget;
2897+
const savedExpressions = this._currentScopeCodeFlowExpressions;
28952898
this._currentContinueTarget = preLoopLabel;
28962899
this._currentBreakTarget = postLoopLabel;
2900+
this._currentScopeCodeFlowExpressions = new Set<string>();
28972901

28982902
callback();
28992903

2904+
preLoopLabel.expressions = this._currentScopeCodeFlowExpressions;
2905+
2906+
if (savedExpressions) {
2907+
this._currentScopeCodeFlowExpressions.forEach((value) => {
2908+
savedExpressions.add(value);
2909+
});
2910+
}
2911+
2912+
this._currentScopeCodeFlowExpressions = savedExpressions;
29002913
this._currentContinueTarget = savedContinueTarget;
29012914
this._currentBreakTarget = savedBreakTarget;
29022915
}

packages/pyright-internal/src/analyzer/codeFlow.ts

+8
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ export interface FlowLabel extends FlowNode {
7171
antecedents: FlowNode[];
7272
}
7373

74+
export interface FlowLoopLabel extends FlowLabel {
75+
// Set of all expressions that require code flow analysis
76+
// through the loop to determine their types. If an expression
77+
// is not within this map, loop analysis can be skipped and
78+
// determined from the first antecedent only.
79+
expressions: Set<string> | undefined;
80+
}
81+
7482
// FlowAssignment represents a node that assigns a value.
7583
export interface FlowAssignment extends FlowNode {
7684
node: CodeFlowReferenceExpressionNode;

packages/pyright-internal/src/analyzer/typeEvaluator.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import {
9393
FlowExhaustedMatch,
9494
FlowFlags,
9595
FlowLabel,
96+
FlowLoopLabel,
9697
FlowNarrowForPattern,
9798
FlowNode,
9899
FlowPostContextManagerLabel,
@@ -17576,7 +17577,18 @@ export function createTypeEvaluator(
1757617577
}
1757717578

1757817579
if (curFlowNode.flags & FlowFlags.LoopLabel) {
17579-
const loopNode = curFlowNode as FlowLabel;
17580+
const loopNode = curFlowNode as FlowLoopLabel;
17581+
17582+
// Is the current symbol modified in any way within the
17583+
// loop? If not, we can skip all processing within the loop
17584+
// and assume that the type comes from the first antecedent,
17585+
// which feeds the loop.
17586+
if (referenceKey) {
17587+
if (!loopNode.expressions || !loopNode.expressions.has(referenceKey)) {
17588+
curFlowNode = loopNode.antecedents[0];
17589+
continue;
17590+
}
17591+
}
1758017592

1758117593
let firstWasIncomplete = false;
1758217594
let isFirstTimeInLoop = false;

0 commit comments

Comments
 (0)