Skip to content

Commit 07c192e

Browse files
committed
Fixed an issue with ReduceLabel flows spoiling node reachability cache
1 parent 7507b05 commit 07c192e

File tree

4 files changed

+393
-8
lines changed

4 files changed

+393
-8
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,6 +1426,13 @@ const enum IntrinsicTypeKind {
14261426
NoInfer,
14271427
}
14281428

1429+
const enum FlowNodeReachableCacheFlags {
1430+
None = 0,
1431+
Read = 1 << 0,
1432+
Write = 1 << 1,
1433+
ReadWrite = Read | Write,
1434+
}
1435+
14291436
const intrinsicTypeKinds: ReadonlyMap<string, IntrinsicTypeKind> = new Map(Object.entries({
14301437
Uppercase: IntrinsicTypeKind.Uppercase,
14311438
Lowercase: IntrinsicTypeKind.Lowercase,
@@ -28104,7 +28111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2810428111
}
2810528112

2810628113
function isReachableFlowNode(flow: FlowNode) {
28107-
const result = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ false);
28114+
const result = isReachableFlowNodeWorker(flow, FlowNodeReachableCacheFlags.ReadWrite);
2810828115
lastFlowNode = flow;
2810928116
lastFlowNodeReachable = result;
2811028117
return result;
@@ -28118,19 +28125,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2811828125
);
2811928126
}
2812028127

28121-
function isReachableFlowNodeWorker(flow: FlowNode, noCacheCheck: boolean): boolean {
28128+
function isReachableFlowNodeWorker(flow: FlowNode, cacheFlags: FlowNodeReachableCacheFlags): boolean {
2812228129
while (true) {
2812328130
if (flow === lastFlowNode) {
2812428131
return lastFlowNodeReachable;
2812528132
}
2812628133
const flags = flow.flags;
2812728134
if (flags & FlowFlags.Shared) {
28128-
if (!noCacheCheck) {
28135+
if (cacheFlags & FlowNodeReachableCacheFlags.Read) {
2812928136
const id = getFlowNodeId(flow);
28130-
const reachable = flowNodeReachable[id];
28131-
return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ true));
28137+
let reachable = flowNodeReachable[id];
28138+
if (reachable !== undefined) {
28139+
return reachable;
28140+
}
28141+
reachable = isReachableFlowNodeWorker(flow, cacheFlags & ~FlowNodeReachableCacheFlags.Read);
28142+
if (cacheFlags & FlowNodeReachableCacheFlags.Write) {
28143+
flowNodeReachable[id] = reachable;
28144+
}
28145+
return reachable;
2813228146
}
28133-
noCacheCheck = false;
28147+
cacheFlags |= FlowNodeReachableCacheFlags.Read;
2813428148
}
2813528149
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
2813628150
flow = (flow as FlowAssignment | FlowCondition | FlowArrayMutation).antecedent;
@@ -28153,7 +28167,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2815328167
}
2815428168
else if (flags & FlowFlags.BranchLabel) {
2815528169
// A branching point is reachable if any branch is reachable.
28156-
return some((flow as FlowLabel).antecedent, f => isReachableFlowNodeWorker(f, /*noCacheCheck*/ false));
28170+
return some((flow as FlowLabel).antecedent, f => isReachableFlowNodeWorker(f, cacheFlags));
2815728171
}
2815828172
else if (flags & FlowFlags.LoopLabel) {
2815928173
const antecedents = (flow as FlowLabel).antecedent;
@@ -28178,7 +28192,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2817828192
const target = (flow as FlowReduceLabel).node.target;
2817928193
const saveAntecedents = target.antecedent;
2818028194
target.antecedent = (flow as FlowReduceLabel).node.antecedents;
28181-
const result = isReachableFlowNodeWorker((flow as FlowReduceLabel).antecedent, /*noCacheCheck*/ false);
28195+
const result = isReachableFlowNodeWorker((flow as FlowReduceLabel).antecedent, FlowNodeReachableCacheFlags.None);
2818228196
target.antecedent = saveAntecedents;
2818328197
return result;
2818428198
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//// [tests/cases/compiler/reachabilityChecks9.ts] ////
2+
3+
=== reachabilityChecks9.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/61259
5+
6+
const a = (v: 1 | 2) => {
7+
>a : Symbol(a, Decl(reachabilityChecks9.ts, 2, 5))
8+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11))
9+
10+
try {
11+
switch (v) {
12+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11))
13+
14+
case 1:
15+
return v;
16+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11))
17+
18+
case 2:
19+
return v;
20+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11))
21+
}
22+
} finally {
23+
console.log("exit");
24+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
25+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
26+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
27+
}
28+
};
29+
30+
const b = (v: number) => {
31+
>b : Symbol(b, Decl(reachabilityChecks9.ts, 15, 5))
32+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11))
33+
34+
try {
35+
switch (v) {
36+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11))
37+
38+
case 1:
39+
return v;
40+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11))
41+
42+
default:
43+
return v;
44+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11))
45+
}
46+
} finally {
47+
console.log("exit");
48+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
49+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
50+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
51+
}
52+
};
53+
54+
const c = (v: 1 | 2) => {
55+
>c : Symbol(c, Decl(reachabilityChecks9.ts, 28, 5))
56+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11))
57+
58+
try {
59+
switch (v) {
60+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11))
61+
62+
case 1:
63+
return v;
64+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11))
65+
66+
case 2:
67+
return v;
68+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11))
69+
}
70+
} finally {
71+
if (Math.random()) {
72+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
73+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
74+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
75+
76+
console.log("exit");
77+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
78+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
79+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
80+
}
81+
}
82+
};
83+
84+
const d = (v: number) => {
85+
>d : Symbol(d, Decl(reachabilityChecks9.ts, 43, 5))
86+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11))
87+
88+
try {
89+
switch (v) {
90+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11))
91+
92+
case 1:
93+
return v;
94+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11))
95+
96+
default:
97+
return v;
98+
>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11))
99+
}
100+
} finally {
101+
if (Math.random()) {
102+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
103+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
104+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
105+
106+
console.log("exit");
107+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
108+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
109+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
110+
}
111+
}
112+
};
113+
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
//// [tests/cases/compiler/reachabilityChecks9.ts] ////
2+
3+
=== reachabilityChecks9.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/61259
5+
6+
const a = (v: 1 | 2) => {
7+
>a : (v: 1 | 2) => 1 | 2
8+
> : ^ ^^ ^^^^^^^^^^
9+
>(v: 1 | 2) => { try { switch (v) { case 1: return v; case 2: return v; } } finally { console.log("exit"); }} : (v: 1 | 2) => 1 | 2
10+
> : ^ ^^ ^^^^^^^^^^
11+
>v : 1 | 2
12+
> : ^^^^^
13+
14+
try {
15+
switch (v) {
16+
>v : 1 | 2
17+
> : ^^^^^
18+
19+
case 1:
20+
>1 : 1
21+
> : ^
22+
23+
return v;
24+
>v : 1
25+
> : ^
26+
27+
case 2:
28+
>2 : 2
29+
> : ^
30+
31+
return v;
32+
>v : 2
33+
> : ^
34+
}
35+
} finally {
36+
console.log("exit");
37+
>console.log("exit") : void
38+
> : ^^^^
39+
>console.log : (...data: any[]) => void
40+
> : ^^^^ ^^ ^^^^^
41+
>console : Console
42+
> : ^^^^^^^
43+
>log : (...data: any[]) => void
44+
> : ^^^^ ^^ ^^^^^
45+
>"exit" : "exit"
46+
> : ^^^^^^
47+
}
48+
};
49+
50+
const b = (v: number) => {
51+
>b : (v: number) => number
52+
> : ^ ^^ ^^^^^^^^^^^
53+
>(v: number) => { try { switch (v) { case 1: return v; default: return v; } } finally { console.log("exit"); }} : (v: number) => number
54+
> : ^ ^^ ^^^^^^^^^^^
55+
>v : number
56+
> : ^^^^^^
57+
58+
try {
59+
switch (v) {
60+
>v : number
61+
> : ^^^^^^
62+
63+
case 1:
64+
>1 : 1
65+
> : ^
66+
67+
return v;
68+
>v : 1
69+
> : ^
70+
71+
default:
72+
return v;
73+
>v : number
74+
> : ^^^^^^
75+
}
76+
} finally {
77+
console.log("exit");
78+
>console.log("exit") : void
79+
> : ^^^^
80+
>console.log : (...data: any[]) => void
81+
> : ^^^^ ^^ ^^^^^
82+
>console : Console
83+
> : ^^^^^^^
84+
>log : (...data: any[]) => void
85+
> : ^^^^ ^^ ^^^^^
86+
>"exit" : "exit"
87+
> : ^^^^^^
88+
}
89+
};
90+
91+
const c = (v: 1 | 2) => {
92+
>c : (v: 1 | 2) => 1 | 2
93+
> : ^ ^^ ^^^^^^^^^^
94+
>(v: 1 | 2) => { try { switch (v) { case 1: return v; case 2: return v; } } finally { if (Math.random()) { console.log("exit"); } }} : (v: 1 | 2) => 1 | 2
95+
> : ^ ^^ ^^^^^^^^^^
96+
>v : 1 | 2
97+
> : ^^^^^
98+
99+
try {
100+
switch (v) {
101+
>v : 1 | 2
102+
> : ^^^^^
103+
104+
case 1:
105+
>1 : 1
106+
> : ^
107+
108+
return v;
109+
>v : 1
110+
> : ^
111+
112+
case 2:
113+
>2 : 2
114+
> : ^
115+
116+
return v;
117+
>v : 2
118+
> : ^
119+
}
120+
} finally {
121+
if (Math.random()) {
122+
>Math.random() : number
123+
> : ^^^^^^
124+
>Math.random : () => number
125+
> : ^^^^^^
126+
>Math : Math
127+
> : ^^^^
128+
>random : () => number
129+
> : ^^^^^^
130+
131+
console.log("exit");
132+
>console.log("exit") : void
133+
> : ^^^^
134+
>console.log : (...data: any[]) => void
135+
> : ^^^^ ^^ ^^^^^
136+
>console : Console
137+
> : ^^^^^^^
138+
>log : (...data: any[]) => void
139+
> : ^^^^ ^^ ^^^^^
140+
>"exit" : "exit"
141+
> : ^^^^^^
142+
}
143+
}
144+
};
145+
146+
const d = (v: number) => {
147+
>d : (v: number) => number
148+
> : ^ ^^ ^^^^^^^^^^^
149+
>(v: number) => { try { switch (v) { case 1: return v; default: return v; } } finally { if (Math.random()) { console.log("exit"); } }} : (v: number) => number
150+
> : ^ ^^ ^^^^^^^^^^^
151+
>v : number
152+
> : ^^^^^^
153+
154+
try {
155+
switch (v) {
156+
>v : number
157+
> : ^^^^^^
158+
159+
case 1:
160+
>1 : 1
161+
> : ^
162+
163+
return v;
164+
>v : 1
165+
> : ^
166+
167+
default:
168+
return v;
169+
>v : number
170+
> : ^^^^^^
171+
}
172+
} finally {
173+
if (Math.random()) {
174+
>Math.random() : number
175+
> : ^^^^^^
176+
>Math.random : () => number
177+
> : ^^^^^^
178+
>Math : Math
179+
> : ^^^^
180+
>random : () => number
181+
> : ^^^^^^
182+
183+
console.log("exit");
184+
>console.log("exit") : void
185+
> : ^^^^
186+
>console.log : (...data: any[]) => void
187+
> : ^^^^ ^^ ^^^^^
188+
>console : Console
189+
> : ^^^^^^^
190+
>log : (...data: any[]) => void
191+
> : ^^^^ ^^ ^^^^^
192+
>"exit" : "exit"
193+
> : ^^^^^^
194+
}
195+
}
196+
};
197+

0 commit comments

Comments
 (0)