@@ -233,13 +233,12 @@ def accept_loop(self, body: Node, else_body: Node = None, *,
233233 """
234234 # The outer frame accumulates the results of all iterations
235235 with self .binder .frame_context (can_skip = False ):
236- self .binder .push_loop_frame ()
237236 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 ):
239239 self .accept (body )
240240 if not self .binder .last_pop_changed :
241241 break
242- self .binder .pop_loop_frame ()
243242 if exit_condition :
244243 _ , else_map = self .find_isinstance_check (exit_condition )
245244 self .push_type_map (else_map )
@@ -1639,19 +1638,17 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
16391638 # This one gets all possible states after the try block exited abnormally
16401639 # (by exception, return, break, etc.)
16411640 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 ))
16421649 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 )
16511650 # First we check finally_body is type safe on all abnormal exit paths
16521651 self .accept (s .finally_body )
1653- else :
1654- self .visit_try_without_finally (s )
16551652
16561653 if s .finally_body :
16571654 # Then we try again for the more restricted set of options
@@ -1669,7 +1666,7 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
16691666
16701667 return None
16711668
1672- def visit_try_without_finally (self , s : TryStmt ) -> None :
1669+ def visit_try_without_finally (self , s : TryStmt , try_frame : bool ) -> None :
16731670 """Type check a try statement, ignoring the finally block.
16741671
16751672 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:
16801677 # This frame will run the else block if the try fell through.
16811678 # In that case, control flow continues to the parent of what
16821679 # 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 ):
16841681 # This frame receives exit via exception, and runs exception handlers
16851682 with self .binder .frame_context (can_skip = False , fall_through = 2 ):
16861683 # 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 ):
16901685 self .accept (s .body )
1691- self .binder .try_frames .remove (len (self .binder .frames ) - 2 )
16921686 for i in range (len (s .handlers )):
16931687 with self .binder .frame_context (can_skip = True , fall_through = 4 ):
16941688 if s .types [i ]:
@@ -2018,13 +2012,11 @@ def visit_member_expr(self, e: MemberExpr) -> Type:
20182012 return self .expr_checker .visit_member_expr (e )
20192013
20202014 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 ()
20232016 return None
20242017
20252018 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 ()
20282020 return None
20292021
20302022 def visit_int_expr (self , e : IntExpr ) -> Type :
0 commit comments