Skip to content

Commit 5a6c811

Browse files
[ty] Avoid storing redundant reachability indexes (#25453)
## Summary When all reachability nodes for a scope are retained, their IDs already match their positions in the stored node array. In that case, this PR avoids storing an extra bit vector for mapping node IDs back to the array. We only allocate that mapping when unused intermediate nodes were removed. This reduces retained memory for reachability constraints while preserving the existing compaction for scopes that need it. It also moves node lookup into `ReachabilityConstraints::get_interior_node`, so both paths use the same lookup logic.
1 parent a8aceb1 commit 5a6c811

2 files changed

Lines changed: 29 additions & 34 deletions

File tree

crates/ty_python_core/src/reachability_constraints.rs

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -144,29 +144,31 @@ pub struct ReachabilityConstraints {
144144
/// A bit vector indicating which interior TDD nodes were marked as used. This is indexed by
145145
/// the node's [`ScopedReachabilityConstraintId`]. The rank of the corresponding bit gives the
146146
/// index of that node in the `used_interiors` vector.
147-
used_indices: RankBitBox,
147+
///
148+
/// If all interior nodes were retained, the original ID can be used directly instead.
149+
used_indices: Option<Box<RankBitBox>>,
148150
}
149151

150152
impl ReachabilityConstraints {
151153
/// Look up an interior node by its constraint ID.
152154
pub fn get_interior_node(&self, id: ScopedReachabilityConstraintId) -> InteriorNode {
153155
debug_assert!(!id.is_terminal());
154156
let raw_index = id.as_u32() as usize;
155-
debug_assert!(
156-
self.used_indices().get_bit(raw_index).unwrap_or(false),
157-
"all used reachability constraints should have been marked as used",
158-
);
159-
let index = self.used_indices().rank(raw_index) as usize;
160-
self.used_interiors()[index]
157+
if let Some(used_indices) = &self.used_indices {
158+
debug_assert!(
159+
used_indices.get_bit(raw_index).unwrap_or(false),
160+
"all used reachability constraints should have been marked as used",
161+
);
162+
let index = used_indices.rank(raw_index) as usize;
163+
self.used_interiors[index]
164+
} else {
165+
self.used_interiors[raw_index]
166+
}
161167
}
162168

163169
pub fn used_interiors(&self) -> &[InteriorNode] {
164170
&self.used_interiors
165171
}
166-
167-
pub fn used_indices(&self) -> &RankBitBox {
168-
&self.used_indices
169-
}
170172
}
171173

172174
#[derive(Debug, Default, PartialEq, Eq)]
@@ -193,14 +195,21 @@ pub struct ReachabilityConstraintsBuilder {
193195

194196
impl ReachabilityConstraintsBuilder {
195197
pub(crate) fn build(self) -> ReachabilityConstraints {
196-
let used_indices = RankBitBox::from_bits(self.interior_used.iter().copied());
197-
let used_interiors = (self.interiors.into_iter())
198-
.zip(self.interior_used)
199-
.filter_map(|(interior, used)| used.then_some(interior))
200-
.collect();
201-
ReachabilityConstraints {
202-
used_interiors,
203-
used_indices,
198+
if self.interior_used.iter().all(|used| *used) {
199+
ReachabilityConstraints {
200+
used_interiors: self.interiors.raw.into_boxed_slice(),
201+
used_indices: None,
202+
}
203+
} else {
204+
let used_indices = RankBitBox::from_bits(self.interior_used.iter().copied());
205+
let used_interiors = (self.interiors.into_iter())
206+
.zip(self.interior_used)
207+
.filter_map(|(interior, used)| used.then_some(interior))
208+
.collect();
209+
ReachabilityConstraints {
210+
used_interiors,
211+
used_indices: Some(Box::new(used_indices)),
212+
}
204213
}
205214
}
206215

crates/ty_python_semantic/src/reachability.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -629,21 +629,7 @@ impl<'db> ReachabilityConstraintsExtension<'db> for ReachabilityConstraints {
629629
Id::ALWAYS_TRUE => return Truthiness::AlwaysTrue,
630630
Id::AMBIGUOUS => return Truthiness::Ambiguous,
631631
Id::ALWAYS_FALSE => return Truthiness::AlwaysFalse,
632-
_ => {
633-
// `id` gives us the index of this node in the IndexVec that we used when
634-
// constructing this BDD. When finalizing the builder, we threw away any
635-
// interior nodes that weren't marked as used. The `used_indices` bit vector
636-
// lets us verify that this node was marked as used, and the rank of that bit
637-
// in the bit vector tells us where this node lives in the "condensed"
638-
// `used_interiors` vector.
639-
let raw_index = id.as_u32() as usize;
640-
debug_assert!(
641-
self.used_indices().get_bit(raw_index).unwrap_or(false),
642-
"all used reachability constraints should have been marked as used",
643-
);
644-
let index = self.used_indices().rank(raw_index) as usize;
645-
self.used_interiors()[index]
646-
}
632+
_ => self.get_interior_node(id),
647633
};
648634
let predicate = &predicates[node.atom()];
649635
match analyze_single(db, predicate) {

0 commit comments

Comments
 (0)