@@ -233,13 +233,12 @@ def accept_loop(self, body: Node, else_body: Node = None, *,
233
233
"""
234
234
# The outer frame accumulates the results of all iterations
235
235
with self .binder .frame_context (can_skip = False ):
236
- self .binder .push_loop_frame ()
237
236
while True :
238
- with self .binder .frame_context (can_skip = True ):
237
+ with self .binder .frame_context (can_skip = True ,
238
+ break_frame = 2 , continue_frame = 1 ):
239
239
self .accept (body )
240
240
if not self .binder .last_pop_changed :
241
241
break
242
- self .binder .pop_loop_frame ()
243
242
if exit_condition :
244
243
_ , else_map = self .find_isinstance_check (exit_condition )
245
244
self .push_type_map (else_map )
@@ -1639,19 +1638,17 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
1639
1638
# This one gets all possible states after the try block exited abnormally
1640
1639
# (by exception, return, break, etc.)
1641
1640
with self .binder .frame_context (can_skip = False , fall_through = 0 ):
1641
+ # Not only might the body of the try statement exit
1642
+ # abnormally, but so might an exception handler or else
1643
+ # clause. The finally clause runs in *all* cases, so we
1644
+ # need an outer try frame to catch all intermediate states
1645
+ # in case an exception is raised during an except or else
1646
+ # clause. As an optimization, only create the outer try
1647
+ # frame when there actually is a finally clause.
1648
+ self .visit_try_without_finally (s , try_frame = bool (s .finally_body ))
1642
1649
if s .finally_body :
1643
- # Not only might the body of the try statement exit abnormally,
1644
- # but so might an exception handler or else clause. The finally
1645
- # clause runs in *all* cases, so we need an outer try frame to
1646
- # catch all intermediate states in case an exception is raised
1647
- # during an except or else clause.
1648
- self .binder .try_frames .add (len (self .binder .frames ) - 1 )
1649
- self .visit_try_without_finally (s )
1650
- self .binder .try_frames .remove (len (self .binder .frames ) - 1 )
1651
1650
# First we check finally_body is type safe on all abnormal exit paths
1652
1651
self .accept (s .finally_body )
1653
- else :
1654
- self .visit_try_without_finally (s )
1655
1652
1656
1653
if s .finally_body :
1657
1654
# Then we try again for the more restricted set of options
@@ -1669,7 +1666,7 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
1669
1666
1670
1667
return None
1671
1668
1672
- def visit_try_without_finally (self , s : TryStmt ) -> None :
1669
+ def visit_try_without_finally (self , s : TryStmt , try_frame : bool ) -> None :
1673
1670
"""Type check a try statement, ignoring the finally block.
1674
1671
1675
1672
On entry, the top frame should receive all flow that exits the
@@ -1680,15 +1677,12 @@ def visit_try_without_finally(self, s: TryStmt) -> None:
1680
1677
# This frame will run the else block if the try fell through.
1681
1678
# In that case, control flow continues to the parent of what
1682
1679
# was the top frame on entry.
1683
- with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1680
+ with self .binder .frame_context (can_skip = False , fall_through = 2 , try_frame = try_frame ):
1684
1681
# This frame receives exit via exception, and runs exception handlers
1685
1682
with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1686
1683
# Finally, the body of the try statement
1687
- with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1688
- self .binder .try_frames .add (len (self .binder .frames ) - 2 )
1689
- self .binder .allow_jump (- 1 )
1684
+ with self .binder .frame_context (can_skip = False , fall_through = 2 , try_frame = True ):
1690
1685
self .accept (s .body )
1691
- self .binder .try_frames .remove (len (self .binder .frames ) - 2 )
1692
1686
for i in range (len (s .handlers )):
1693
1687
with self .binder .frame_context (can_skip = True , fall_through = 4 ):
1694
1688
if s .types [i ]:
@@ -2018,13 +2012,11 @@ def visit_member_expr(self, e: MemberExpr) -> Type:
2018
2012
return self .expr_checker .visit_member_expr (e )
2019
2013
2020
2014
def visit_break_stmt (self , s : BreakStmt ) -> Type :
2021
- self .binder .allow_jump (self .binder .loop_frames [- 1 ] - 1 )
2022
- self .binder .unreachable ()
2015
+ self .binder .handle_break ()
2023
2016
return None
2024
2017
2025
2018
def visit_continue_stmt (self , s : ContinueStmt ) -> Type :
2026
- self .binder .allow_jump (self .binder .loop_frames [- 1 ])
2027
- self .binder .unreachable ()
2019
+ self .binder .handle_continue ()
2028
2020
return None
2029
2021
2030
2022
def visit_int_expr (self , e : IntExpr ) -> Type :
0 commit comments