@@ -1322,7 +1322,18 @@ impl<'a> FlowTypeEngine<'a> {
13221322 // produces a final type, needs another query first, or reaches a saved
13231323 // continuation point like a branch merge.
13241324 fn evaluate_walk ( & mut self , mut walk : QueryWalk ) -> Result < SchedulerStep , InferFailReason > {
1325+ // Most walks are acyclic, so only start tracking after loop control can
1326+ // feed a later flow node back into an earlier loop label.
1327+ let mut visited_flow_ids = None :: < HashSet < FlowId > > ;
1328+
13251329 loop {
1330+ if let Some ( visited_flow_ids) = & mut visited_flow_ids
1331+ && !visited_flow_ids. insert ( walk. antecedent_flow_id )
1332+ {
1333+ let result_type = get_var_ref_type ( self . db , self . cache , & walk. query . var_ref_id ) ?;
1334+ return Ok ( self . finish_walk ( walk, result_type) ) ;
1335+ }
1336+
13261337 // Replays can suspend a query and later revisit an older antecedent
13271338 // that another query already finished. Use that cached type instead
13281339 // of walking back through the same replay chain again.
@@ -1356,6 +1367,15 @@ impl<'a> FlowTypeEngine<'a> {
13561367 | FlowNodeKind :: Continue
13571368 | FlowNodeKind :: Return
13581369 | FlowNodeKind :: ForIStat ( _) => {
1370+ if matches ! (
1371+ & flow_node. kind,
1372+ FlowNodeKind :: LoopLabel | FlowNodeKind :: Continue
1373+ ) && visited_flow_ids. is_none ( )
1374+ {
1375+ let mut visited = HashSet :: new ( ) ;
1376+ visited. insert ( walk. antecedent_flow_id ) ;
1377+ visited_flow_ids = Some ( visited) ;
1378+ }
13591379 walk. antecedent_flow_id = get_single_antecedent ( flow_node) ?;
13601380 }
13611381 FlowNodeKind :: BranchLabel | FlowNodeKind :: NamedLabel ( _) => {
0 commit comments