@@ -41,22 +41,22 @@ use crate::Db;
4141
4242mod except_handlers;
4343
44- /// Are we in a state where a `break` statement is allowed?
45- #[ derive( Clone , Copy , Debug ) ]
46- enum LoopState {
47- InLoop ,
48- NotInLoop ,
44+ #[ derive( Clone , Debug , Default ) ]
45+ struct Loop {
46+ /// Flow states at each `break` in the current loop.
47+ break_states : Vec < FlowSnapshot > ,
4948}
5049
51- impl LoopState {
52- fn is_inside ( self ) -> bool {
53- matches ! ( self , LoopState :: InLoop )
50+ impl Loop {
51+ fn push_break ( & mut self , state : FlowSnapshot ) {
52+ self . break_states . push ( state )
5453 }
5554}
5655
5756struct ScopeInfo {
5857 file_scope_id : FileScopeId ,
59- loop_state : LoopState ,
58+ /// Current loop state; None if this scope is currently not in a loop
59+ current_loop : Option < Loop > ,
6060}
6161
6262pub ( super ) struct SemanticIndexBuilder < ' db > {
@@ -73,8 +73,6 @@ pub(super) struct SemanticIndexBuilder<'db> {
7373 /// The name of the first function parameter of the innermost function that we're currently visiting.
7474 current_first_parameter_name : Option < & ' db str > ,
7575
76- /// Flow states at each `break` in the current loop.
77- loop_break_states : Vec < FlowSnapshot > ,
7876 /// Per-scope contexts regarding nested `try`/`except` statements
7977 try_node_context_stack_manager : TryNodeContextStackManager ,
8078
@@ -106,7 +104,6 @@ impl<'db> SemanticIndexBuilder<'db> {
106104 current_assignments : vec ! [ ] ,
107105 current_match_case : None ,
108106 current_first_parameter_name : None ,
109- loop_break_states : vec ! [ ] ,
110107 try_node_context_stack_manager : TryNodeContextStackManager :: default ( ) ,
111108
112109 has_future_annotations : false ,
@@ -134,19 +131,20 @@ impl<'db> SemanticIndexBuilder<'db> {
134131 builder
135132 }
136133
137- fn current_scope ( & self ) -> FileScopeId {
138- * self
139- . scope_stack
134+ fn current_scope_info ( & self ) -> & ScopeInfo {
135+ self . scope_stack
140136 . last ( )
141- . map ( |ScopeInfo { file_scope_id, .. } | file_scope_id)
142137 . expect ( "SemanticIndexBuilder should have created a root scope" )
143138 }
144139
145- fn loop_state ( & self ) -> LoopState {
140+ fn current_scope_info_mut ( & mut self ) -> & mut ScopeInfo {
146141 self . scope_stack
147- . last ( )
142+ . last_mut ( )
148143 . expect ( "SemanticIndexBuilder should have created a root scope" )
149- . loop_state
144+ }
145+
146+ fn current_scope ( & self ) -> FileScopeId {
147+ self . current_scope_info ( ) . file_scope_id
150148 }
151149
152150 /// Returns the scope ID of the surrounding class body scope if the current scope
@@ -167,11 +165,21 @@ impl<'db> SemanticIndexBuilder<'db> {
167165 }
168166 }
169167
170- fn set_inside_loop ( & mut self , state : LoopState ) {
171- self . scope_stack
172- . last_mut ( )
173- . expect ( "Always to have a root scope" )
174- . loop_state = state;
168+ /// Push a new loop, returning the outer loop, if any.
169+ fn push_loop ( & mut self ) -> Option < Loop > {
170+ self . current_scope_info_mut ( )
171+ . current_loop
172+ . replace ( Loop :: default ( ) )
173+ }
174+
175+ /// Pop a loop, replacing with the previous saved outer loop, if any.
176+ fn pop_loop ( & mut self , outer_loop : Option < Loop > ) -> Loop {
177+ std:: mem:: replace ( & mut self . current_scope_info_mut ( ) . current_loop , outer_loop)
178+ . expect ( "pop_loop() should not be called without a prior push_loop()" )
179+ }
180+
181+ fn current_loop_mut ( & mut self ) -> Option < & mut Loop > {
182+ self . current_scope_info_mut ( ) . current_loop . as_mut ( )
175183 }
176184
177185 fn push_scope ( & mut self , node : NodeWithScopeRef ) {
@@ -204,7 +212,7 @@ impl<'db> SemanticIndexBuilder<'db> {
204212
205213 self . scope_stack . push ( ScopeInfo {
206214 file_scope_id,
207- loop_state : LoopState :: NotInLoop ,
215+ current_loop : None ,
208216 } ) ;
209217 }
210218
@@ -1208,15 +1216,9 @@ where
12081216 . current_visibility_constraints_mut ( )
12091217 . add_atom ( later_predicate_id) ;
12101218
1211- // Save aside any break states from an outer loop
1212- let saved_break_states = std:: mem:: take ( & mut self . loop_break_states ) ;
1213-
1214- // TODO: definitions created inside the body should be fully visible
1215- // to other statements/expressions inside the body --Alex/Carl
1216- let outer_loop_state = self . loop_state ( ) ;
1217- self . set_inside_loop ( LoopState :: InLoop ) ;
1219+ let outer_loop = self . push_loop ( ) ;
12181220 self . visit_body ( body) ;
1219- self . set_inside_loop ( outer_loop_state ) ;
1221+ let loop_state = self . pop_loop ( outer_loop ) ;
12201222
12211223 // If the body is executed, we know that we've evaluated the condition at least
12221224 // once, and that the first evaluation was True. We might not have evaluated the
@@ -1225,11 +1227,6 @@ where
12251227 let body_vis_constraint_id = first_vis_constraint_id;
12261228 self . record_visibility_constraint_id ( body_vis_constraint_id) ;
12271229
1228- // Get the break states from the body of this loop, and restore the saved outer
1229- // ones.
1230- let break_states =
1231- std:: mem:: replace ( & mut self . loop_break_states , saved_break_states) ;
1232-
12331230 // We execute the `else` once the condition evaluates to false. This could happen
12341231 // without ever executing the body, if the condition is false the first time it's
12351232 // tested. So the starting flow state of the `else` clause is the union of:
@@ -1250,7 +1247,7 @@ where
12501247
12511248 // Breaking out of a while loop bypasses the `else` clause, so merge in the break
12521249 // states after visiting `else`.
1253- for break_state in break_states {
1250+ for break_state in loop_state . break_states {
12541251 let snapshot = self . flow_snapshot ( ) ;
12551252 self . flow_restore ( break_state) ;
12561253 self . record_visibility_constraint_id ( body_vis_constraint_id) ;
@@ -1298,7 +1295,6 @@ where
12981295 self . record_ambiguous_visibility ( ) ;
12991296
13001297 let pre_loop = self . flow_snapshot ( ) ;
1301- let saved_break_states = std:: mem:: take ( & mut self . loop_break_states ) ;
13021298
13031299 let current_assignment = match & * * target {
13041300 ast:: Expr :: List ( _) | ast:: Expr :: Tuple ( _) => Some ( CurrentAssignment :: For {
@@ -1346,16 +1342,9 @@ where
13461342 self . pop_assignment ( ) ;
13471343 }
13481344
1349- // TODO: Definitions created by loop variables
1350- // (and definitions created inside the body)
1351- // are fully visible to other statements/expressions inside the body --Alex/Carl
1352- let outer_loop_state = self . loop_state ( ) ;
1353- self . set_inside_loop ( LoopState :: InLoop ) ;
1345+ let outer_loop = self . push_loop ( ) ;
13541346 self . visit_body ( body) ;
1355- self . set_inside_loop ( outer_loop_state) ;
1356-
1357- let break_states =
1358- std:: mem:: replace ( & mut self . loop_break_states , saved_break_states) ;
1347+ let this_loop = self . pop_loop ( outer_loop) ;
13591348
13601349 // We may execute the `else` clause without ever executing the body, so merge in
13611350 // the pre-loop state before visiting `else`.
@@ -1364,7 +1353,7 @@ where
13641353
13651354 // Breaking out of a `for` loop bypasses the `else` clause, so merge in the break
13661355 // states after visiting `else`.
1367- for break_state in break_states {
1356+ for break_state in this_loop . break_states {
13681357 self . flow_merge ( break_state) ;
13691358 }
13701359 }
@@ -1547,8 +1536,9 @@ where
15471536 }
15481537
15491538 ast:: Stmt :: Break ( _) => {
1550- if self . loop_state ( ) . is_inside ( ) {
1551- self . loop_break_states . push ( self . flow_snapshot ( ) ) ;
1539+ let snapshot = self . flow_snapshot ( ) ;
1540+ if let Some ( current_loop) = self . current_loop_mut ( ) {
1541+ current_loop. push_break ( snapshot)
15521542 }
15531543 // Everything in the current block after a terminal statement is unreachable.
15541544 self . mark_unreachable ( ) ;
0 commit comments