Skip to content

Commit 15885c6

Browse files
committed
consume other SymbolState in merge()
1 parent 8b38547 commit 15885c6

File tree

3 files changed

+44
-38
lines changed

3 files changed

+44
-38
lines changed

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ impl<'db> SemanticIndexBuilder<'db> {
155155
self.current_use_def_map_mut().restore(state);
156156
}
157157

158-
fn flow_merge(&mut self, state: &FlowSnapshot) {
158+
fn flow_merge(&mut self, state: FlowSnapshot) {
159159
self.current_use_def_map_mut().merge(state);
160160
}
161161

@@ -497,7 +497,7 @@ where
497497
self.visit_elif_else_clause(clause);
498498
}
499499
for post_clause_state in post_clauses {
500-
self.flow_merge(&post_clause_state);
500+
self.flow_merge(post_clause_state);
501501
}
502502
let has_else = node
503503
.elif_else_clauses
@@ -506,7 +506,7 @@ where
506506
if !has_else {
507507
// if there's no else clause, then it's possible we took none of the branches,
508508
// and the pre_if state can reach here
509-
self.flow_merge(&pre_if);
509+
self.flow_merge(pre_if);
510510
}
511511
}
512512
ast::Stmt::While(node) => {
@@ -524,13 +524,13 @@ where
524524

525525
// We may execute the `else` clause without ever executing the body, so merge in
526526
// the pre-loop state before visiting `else`.
527-
self.flow_merge(&pre_loop);
527+
self.flow_merge(pre_loop);
528528
self.visit_body(&node.orelse);
529529

530530
// Breaking out of a while loop bypasses the `else` clause, so merge in the break
531531
// states after visiting `else`.
532532
for break_state in break_states {
533-
self.flow_merge(&break_state);
533+
self.flow_merge(break_state);
534534
}
535535
}
536536
ast::Stmt::Break(_) => {
@@ -640,7 +640,7 @@ where
640640
let post_body = self.flow_snapshot();
641641
self.flow_restore(pre_if);
642642
self.visit_expr(orelse);
643-
self.flow_merge(&post_body);
643+
self.flow_merge(post_body);
644644
}
645645
ast::Expr::ListComp(
646646
list_comprehension @ ast::ExprListComp {

crates/red_knot_python_semantic/src/semantic_index/use_def.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ impl<'db> UseDefMapBuilder<'db> {
334334
/// Merge the given snapshot into the current state, reflecting that we might have taken either
335335
/// path to get here. The new visible-definitions state for each symbol should include
336336
/// definitions from both the prior state and the snapshot.
337-
pub(super) fn merge(&mut self, snapshot: &FlowSnapshot) {
337+
pub(super) fn merge(&mut self, snapshot: FlowSnapshot) {
338338
// The tricky thing about merging two Ranges pointing into `all_definitions` is that if the
339339
// two Ranges aren't already adjacent in `all_definitions`, we will have to copy at least
340340
// one or the other of the ranges to the end of `all_definitions` so as to make them
@@ -348,9 +348,10 @@ impl<'db> UseDefMapBuilder<'db> {
348348
// greater than the number of known symbols in a previously-taken snapshot.
349349
debug_assert!(self.definitions_by_symbol.len() >= snapshot.definitions_by_symbol.len());
350350

351-
for (symbol_id, current) in self.definitions_by_symbol.iter_mut_enumerated() {
352-
if let Some(snapshot) = snapshot.definitions_by_symbol.get(symbol_id) {
353-
*current = SymbolState::merge(current, snapshot);
351+
let mut snapshot_definitions_iter = snapshot.definitions_by_symbol.into_iter();
352+
for current in &mut self.definitions_by_symbol {
353+
if let Some(snapshot) = snapshot_definitions_iter.next() {
354+
current.merge(snapshot);
354355
} else {
355356
// Symbol not present in snapshot, so it's unbound from that path.
356357
current.add_unbound();

crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ const INLINE_CONSTRAINT_BLOCKS: usize = 2;
6666
const INLINE_VISIBLE_DEFINITIONS_PER_SYMBOL: usize = 4;
6767

6868
/// One [`BitSet`] of applicable [`ScopedConstraintId`] per visible definition.
69-
type Constraints =
70-
SmallVec<[BitSet<INLINE_CONSTRAINT_BLOCKS>; INLINE_VISIBLE_DEFINITIONS_PER_SYMBOL]>;
69+
type InlineConstraintArray =
70+
[BitSet<INLINE_CONSTRAINT_BLOCKS>; INLINE_VISIBLE_DEFINITIONS_PER_SYMBOL];
71+
type Constraints = SmallVec<InlineConstraintArray>;
7172
type ConstraintsIterator<'a> = std::slice::Iter<'a, BitSet<INLINE_CONSTRAINT_BLOCKS>>;
73+
type ConstraintsIntoIterator = smallvec::IntoIter<InlineConstraintArray>;
7274

7375
/// Visible definitions and narrowing constraints for a single symbol at some point in control flow.
7476
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -126,17 +128,18 @@ impl SymbolState {
126128
}
127129
}
128130

129-
/// Merge two [`SymbolState`] and return the result.
130-
pub(super) fn merge(a: &SymbolState, b: &SymbolState) -> SymbolState {
131-
let mut merged = Self {
131+
/// Merge another [`SymbolState`] into this one.
132+
pub(super) fn merge(&mut self, b: SymbolState) {
133+
let mut a = Self {
132134
visible_definitions: Definitions::default(),
133135
constraints: Constraints::default(),
134-
may_be_unbound: a.may_be_unbound || b.may_be_unbound,
136+
may_be_unbound: self.may_be_unbound || b.may_be_unbound,
135137
};
138+
std::mem::swap(&mut a, self);
136139
let mut a_defs_iter = a.visible_definitions.iter();
137140
let mut b_defs_iter = b.visible_definitions.iter();
138-
let mut a_constraints_iter = a.constraints.iter();
139-
let mut b_constraints_iter = b.constraints.iter();
141+
let mut a_constraints_iter = a.constraints.into_iter();
142+
let mut b_constraints_iter = b.constraints.into_iter();
140143

141144
let mut opt_a_def: Option<u32> = a_defs_iter.next();
142145
let mut opt_b_def: Option<u32> = b_defs_iter.next();
@@ -147,8 +150,8 @@ impl SymbolState {
147150
// of the constraints from the two paths; a constraint that applies from only one possible
148151
// path is irrelevant.
149152

150-
// Helper to push `def`, with constraints in `constraints_iter`, onto merged`.
151-
let push = |def, constraints_iter: &mut ConstraintsIterator, merged: &mut Self| {
153+
// Helper to push `def`, with constraints in `constraints_iter`, onto `self`.
154+
let push = |def, constraints_iter: &mut ConstraintsIntoIterator, merged: &mut Self| {
152155
merged.visible_definitions.insert(def);
153156
// SAFETY: we only ever create SymbolState with either no definitions and no constraint
154157
// bitsets (`::unbound`) or one definition and one constraint bitset (`::with`), and
@@ -158,59 +161,57 @@ impl SymbolState {
158161
let constraints = constraints_iter
159162
.next()
160163
.expect("definitions and constraints length mismatch");
161-
merged.constraints.push(constraints.clone());
164+
merged.constraints.push(constraints);
162165
};
163166

164167
loop {
165168
match (opt_a_def, opt_b_def) {
166169
(Some(a_def), Some(b_def)) => match a_def.cmp(&b_def) {
167170
std::cmp::Ordering::Less => {
168-
// Next definition ID is only in `a`, push it to `merged` and advance `a`.
169-
push(a_def, &mut a_constraints_iter, &mut merged);
171+
// Next definition ID is only in `a`, push it to `self` and advance `a`.
172+
push(a_def, &mut a_constraints_iter, self);
170173
opt_a_def = a_defs_iter.next();
171174
}
172175
std::cmp::Ordering::Greater => {
173-
// Next definition ID is only in `b`, push it to `merged` and advance `b`.
174-
push(b_def, &mut b_constraints_iter, &mut merged);
176+
// Next definition ID is only in `b`, push it to `self` and advance `b`.
177+
push(b_def, &mut b_constraints_iter, self);
175178
opt_b_def = b_defs_iter.next();
176179
}
177180
std::cmp::Ordering::Equal => {
178-
// Next definition is in both; push to `merged` and intersect constraints.
179-
push(a_def, &mut a_constraints_iter, &mut merged);
181+
// Next definition is in both; push to `self` and intersect constraints.
182+
push(a_def, &mut b_constraints_iter, self);
180183
// SAFETY: we only ever create SymbolState with either no definitions and
181184
// no constraint bitsets (`::unbound`) or one definition and one constraint
182185
// bitset (`::with`), and `::merge` always pushes one definition and one
183186
// constraint bitset together (just below), so the number of definitions
184187
// and the number of constraint bitsets can never get out of sync.
185-
let b_constraints = b_constraints_iter
188+
let a_constraints = a_constraints_iter
186189
.next()
187190
.expect("definitions and constraints length mismatch");
188191
// If the same definition is visible through both paths, any constraint
189192
// that applies on only one path is irrelevant to the resulting type from
190193
// unioning the two paths, so we intersect the constraints.
191-
merged
192-
.constraints
194+
self.constraints
193195
.last_mut()
194196
.unwrap()
195-
.intersect(b_constraints);
197+
.intersect(&a_constraints);
196198
opt_a_def = a_defs_iter.next();
197199
opt_b_def = b_defs_iter.next();
198200
}
199201
},
200202
(Some(a_def), None) => {
201203
// We've exhausted `b`, just push the def from `a` and move on to the next.
202-
push(a_def, &mut a_constraints_iter, &mut merged);
204+
push(a_def, &mut a_constraints_iter, self);
203205
opt_a_def = a_defs_iter.next();
204206
}
205207
(None, Some(b_def)) => {
206208
// We've exhausted `a`, just push the def from `b` and move on to the next.
207-
push(b_def, &mut b_constraints_iter, &mut merged);
209+
push(b_def, &mut b_constraints_iter, self);
208210
opt_b_def = b_defs_iter.next();
209211
}
210212
(None, None) => break,
211213
}
212214
}
213-
merged
214215
}
215216

216217
/// Get iterator over visible definitions with constraints.
@@ -340,7 +341,8 @@ mod tests {
340341
let mut cd0b = SymbolState::with(ScopedDefinitionId::from_u32(0));
341342
cd0b.add_constraint(ScopedConstraintId::from_u32(0));
342343

343-
let cd0 = SymbolState::merge(&cd0a, &cd0b);
344+
cd0a.merge(cd0b);
345+
let mut cd0 = cd0a;
344346
cd0.assert(false, &["0<0>"]);
345347

346348
// merging the same definition with differing constraints drops all constraints
@@ -350,7 +352,8 @@ mod tests {
350352
let mut cd1b = SymbolState::with(ScopedDefinitionId::from_u32(1));
351353
cd1b.add_constraint(ScopedConstraintId::from_u32(2));
352354

353-
let cd1 = SymbolState::merge(&cd1a, &cd1b);
355+
cd1a.merge(cd1b);
356+
let cd1 = cd1a;
354357
cd1.assert(false, &["1<>"]);
355358

356359
// merging a constrained definition with unbound keeps both
@@ -359,11 +362,13 @@ mod tests {
359362

360363
let cd2b = SymbolState::unbound();
361364

362-
let cd2 = SymbolState::merge(&cd2a, &cd2b);
365+
cd2a.merge(cd2b);
366+
let cd2 = cd2a;
363367
cd2.assert(true, &["2<3>"]);
364368

365369
// merging different definitions keeps them each with their existing constraints
366-
let cd = SymbolState::merge(&cd0, &cd2);
370+
cd0.merge(cd2);
371+
let cd = cd0;
367372
cd.assert(true, &["0<0>", "2<3>"]);
368373
}
369374
}

0 commit comments

Comments
 (0)