@@ -226,8 +226,10 @@ def accept(self, node: Node, type_context: Type = None) -> Type:
226
226
else :
227
227
return typ
228
228
229
- def accept_loop (self , body : Node , else_body : Node = None ) -> Type :
229
+ def accept_loop (self , body : Node , else_body : Node = None , * ,
230
+ exit_condition : Node = None ) -> Type :
230
231
"""Repeatedly type check a loop body until the frame doesn't change.
232
+ If exit_condition is set, assume it must be False on exit from the loop.
231
233
232
234
Then check the else_body.
233
235
"""
@@ -240,6 +242,13 @@ def accept_loop(self, body: Node, else_body: Node = None) -> Type:
240
242
if not self .binder .last_pop_changed :
241
243
break
242
244
self .binder .pop_loop_frame ()
245
+ if exit_condition :
246
+ _ , else_map = find_isinstance_check (
247
+ exit_condition , self .type_map , self .typing_mode_weak ()
248
+ )
249
+ if else_map :
250
+ for var , type in else_map .items ():
251
+ self .binder .push (var , type )
243
252
if else_body :
244
253
self .accept (else_body )
245
254
@@ -1465,7 +1474,8 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1465
1474
1466
1475
def visit_while_stmt (self , s : WhileStmt ) -> Type :
1467
1476
"""Type check a while statement."""
1468
- self .accept_loop (IfStmt ([s .expr ], [s .body ], None ), s .else_body )
1477
+ self .accept_loop (IfStmt ([s .expr ], [s .body ], None ), s .else_body ,
1478
+ exit_condition = s .expr )
1469
1479
1470
1480
def visit_operator_assignment_stmt (self ,
1471
1481
s : OperatorAssignmentStmt ) -> Type :
0 commit comments