@@ -41,22 +41,22 @@ use crate::Db;
41
41
42
42
mod except_handlers;
43
43
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 > ,
49
48
}
50
49
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 ) ;
54
53
}
55
54
}
56
55
57
56
struct ScopeInfo {
58
57
file_scope_id : FileScopeId ,
59
- loop_state : LoopState ,
58
+ /// Current loop state; None if we are not currently visiting a loop
59
+ current_loop : Option < Loop > ,
60
60
}
61
61
62
62
pub ( super ) struct SemanticIndexBuilder < ' db > {
@@ -73,8 +73,6 @@ pub(super) struct SemanticIndexBuilder<'db> {
73
73
/// The name of the first function parameter of the innermost function that we're currently visiting.
74
74
current_first_parameter_name : Option < & ' db str > ,
75
75
76
- /// Flow states at each `break` in the current loop.
77
- loop_break_states : Vec < FlowSnapshot > ,
78
76
/// Per-scope contexts regarding nested `try`/`except` statements
79
77
try_node_context_stack_manager : TryNodeContextStackManager ,
80
78
@@ -106,7 +104,6 @@ impl<'db> SemanticIndexBuilder<'db> {
106
104
current_assignments : vec ! [ ] ,
107
105
current_match_case : None ,
108
106
current_first_parameter_name : None ,
109
- loop_break_states : vec ! [ ] ,
110
107
try_node_context_stack_manager : TryNodeContextStackManager :: default ( ) ,
111
108
112
109
has_future_annotations : false ,
@@ -134,19 +131,20 @@ impl<'db> SemanticIndexBuilder<'db> {
134
131
builder
135
132
}
136
133
137
- fn current_scope ( & self ) -> FileScopeId {
138
- * self
139
- . scope_stack
134
+ fn current_scope_info ( & self ) -> & ScopeInfo {
135
+ self . scope_stack
140
136
. last ( )
141
- . map ( |ScopeInfo { file_scope_id, .. } | file_scope_id)
142
137
. expect ( "SemanticIndexBuilder should have created a root scope" )
143
138
}
144
139
145
- fn loop_state ( & self ) -> LoopState {
140
+ fn current_scope_info_mut ( & mut self ) -> & mut ScopeInfo {
146
141
self . scope_stack
147
- . last ( )
142
+ . last_mut ( )
148
143
. 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
150
148
}
151
149
152
150
/// Returns the scope ID of the surrounding class body scope if the current scope
@@ -167,11 +165,21 @@ impl<'db> SemanticIndexBuilder<'db> {
167
165
}
168
166
}
169
167
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 ( )
175
183
}
176
184
177
185
fn push_scope ( & mut self , node : NodeWithScopeRef ) {
@@ -204,7 +212,7 @@ impl<'db> SemanticIndexBuilder<'db> {
204
212
205
213
self . scope_stack . push ( ScopeInfo {
206
214
file_scope_id,
207
- loop_state : LoopState :: NotInLoop ,
215
+ current_loop : None ,
208
216
} ) ;
209
217
}
210
218
@@ -1208,15 +1216,9 @@ where
1208
1216
. current_visibility_constraints_mut ( )
1209
1217
. add_atom ( later_predicate_id) ;
1210
1218
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 ( ) ;
1218
1220
self . visit_body ( body) ;
1219
- self . set_inside_loop ( outer_loop_state ) ;
1221
+ let this_loop = self . pop_loop ( outer_loop ) ;
1220
1222
1221
1223
// If the body is executed, we know that we've evaluated the condition at least
1222
1224
// once, and that the first evaluation was True. We might not have evaluated the
@@ -1225,11 +1227,6 @@ where
1225
1227
let body_vis_constraint_id = first_vis_constraint_id;
1226
1228
self . record_visibility_constraint_id ( body_vis_constraint_id) ;
1227
1229
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
-
1233
1230
// We execute the `else` once the condition evaluates to false. This could happen
1234
1231
// without ever executing the body, if the condition is false the first time it's
1235
1232
// tested. So the starting flow state of the `else` clause is the union of:
@@ -1250,7 +1247,7 @@ where
1250
1247
1251
1248
// Breaking out of a while loop bypasses the `else` clause, so merge in the break
1252
1249
// states after visiting `else`.
1253
- for break_state in break_states {
1250
+ for break_state in this_loop . break_states {
1254
1251
let snapshot = self . flow_snapshot ( ) ;
1255
1252
self . flow_restore ( break_state) ;
1256
1253
self . record_visibility_constraint_id ( body_vis_constraint_id) ;
@@ -1298,7 +1295,6 @@ where
1298
1295
self . record_ambiguous_visibility ( ) ;
1299
1296
1300
1297
let pre_loop = self . flow_snapshot ( ) ;
1301
- let saved_break_states = std:: mem:: take ( & mut self . loop_break_states ) ;
1302
1298
1303
1299
let current_assignment = match & * * target {
1304
1300
ast:: Expr :: List ( _) | ast:: Expr :: Tuple ( _) => Some ( CurrentAssignment :: For {
@@ -1346,16 +1342,9 @@ where
1346
1342
self . pop_assignment ( ) ;
1347
1343
}
1348
1344
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 ( ) ;
1354
1346
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) ;
1359
1348
1360
1349
// We may execute the `else` clause without ever executing the body, so merge in
1361
1350
// the pre-loop state before visiting `else`.
@@ -1364,7 +1353,7 @@ where
1364
1353
1365
1354
// Breaking out of a `for` loop bypasses the `else` clause, so merge in the break
1366
1355
// states after visiting `else`.
1367
- for break_state in break_states {
1356
+ for break_state in this_loop . break_states {
1368
1357
self . flow_merge ( break_state) ;
1369
1358
}
1370
1359
}
@@ -1547,8 +1536,9 @@ where
1547
1536
}
1548
1537
1549
1538
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) ;
1552
1542
}
1553
1543
// Everything in the current block after a terminal statement is unreachable.
1554
1544
self . mark_unreachable ( ) ;
0 commit comments