@@ -237,13 +237,12 @@ def accept_loop(self, body: Node, else_body: Node = None, *,
237
237
"""
238
238
# The outer frame accumulates the results of all iterations
239
239
with self .binder .frame_context (can_skip = False ):
240
- self .binder .push_loop_frame ()
241
240
while True :
242
- with self .binder .frame_context (can_skip = True ):
241
+ with self .binder .frame_context (can_skip = True ,
242
+ break_frame = 2 , continue_frame = 1 ):
243
243
self .accept (body )
244
244
if not self .binder .last_pop_changed :
245
245
break
246
- self .binder .pop_loop_frame ()
247
246
if exit_condition :
248
247
_ , else_map = self .find_isinstance_check (exit_condition )
249
248
self .push_type_map (else_map )
@@ -1544,19 +1543,17 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
1544
1543
# This one gets all possible states after the try block exited abnormally
1545
1544
# (by exception, return, break, etc.)
1546
1545
with self .binder .frame_context (can_skip = False , fall_through = 0 ):
1546
+ # Not only might the body of the try statement exit
1547
+ # abnormally, but so might an exception handler or else
1548
+ # clause. The finally clause runs in *all* cases, so we
1549
+ # need an outer try frame to catch all intermediate states
1550
+ # in case an exception is raised during an except or else
1551
+ # clause. As an optimization, only create the outer try
1552
+ # frame when there actually is a finally clause.
1553
+ self .visit_try_without_finally (s , try_frame = bool (s .finally_body ))
1547
1554
if s .finally_body :
1548
- # Not only might the body of the try statement exit abnormally,
1549
- # but so might an exception handler or else clause. The finally
1550
- # clause runs in *all* cases, so we need an outer try frame to
1551
- # catch all intermediate states in case an exception is raised
1552
- # during an except or else clause.
1553
- self .binder .try_frames .add (len (self .binder .frames ) - 1 )
1554
- self .visit_try_without_finally (s )
1555
- self .binder .try_frames .remove (len (self .binder .frames ) - 1 )
1556
1555
# First we check finally_body is type safe on all abnormal exit paths
1557
1556
self .accept (s .finally_body )
1558
- else :
1559
- self .visit_try_without_finally (s )
1560
1557
1561
1558
if s .finally_body :
1562
1559
# Then we try again for the more restricted set of options
@@ -1574,7 +1571,7 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
1574
1571
1575
1572
return None
1576
1573
1577
- def visit_try_without_finally (self , s : TryStmt ) -> None :
1574
+ def visit_try_without_finally (self , s : TryStmt , try_frame : bool ) -> None :
1578
1575
"""Type check a try statement, ignoring the finally block.
1579
1576
1580
1577
On entry, the top frame should receive all flow that exits the
@@ -1585,15 +1582,12 @@ def visit_try_without_finally(self, s: TryStmt) -> None:
1585
1582
# This frame will run the else block if the try fell through.
1586
1583
# In that case, control flow continues to the parent of what
1587
1584
# was the top frame on entry.
1588
- with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1585
+ with self .binder .frame_context (can_skip = False , fall_through = 2 , try_frame = try_frame ):
1589
1586
# This frame receives exit via exception, and runs exception handlers
1590
1587
with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1591
1588
# Finally, the body of the try statement
1592
- with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1593
- self .binder .try_frames .add (len (self .binder .frames ) - 2 )
1594
- self .binder .allow_jump (- 1 )
1589
+ with self .binder .frame_context (can_skip = False , fall_through = 2 , try_frame = True ):
1595
1590
self .accept (s .body )
1596
- self .binder .try_frames .remove (len (self .binder .frames ) - 2 )
1597
1591
for i in range (len (s .handlers )):
1598
1592
with self .binder .frame_context (can_skip = True , fall_through = 4 ):
1599
1593
if s .types [i ]:
@@ -1839,13 +1833,11 @@ def visit_member_expr(self, e: MemberExpr) -> Type:
1839
1833
return self .expr_checker .visit_member_expr (e )
1840
1834
1841
1835
def visit_break_stmt (self , s : BreakStmt ) -> Type :
1842
- self .binder .allow_jump (self .binder .loop_frames [- 1 ] - 1 )
1843
- self .binder .unreachable ()
1836
+ self .binder .handle_break ()
1844
1837
return None
1845
1838
1846
1839
def visit_continue_stmt (self , s : ContinueStmt ) -> Type :
1847
- self .binder .allow_jump (self .binder .loop_frames [- 1 ])
1848
- self .binder .unreachable ()
1840
+ self .binder .handle_continue ()
1849
1841
return None
1850
1842
1851
1843
def visit_int_expr (self , e : IntExpr ) -> Type :
0 commit comments