use std::collections::hash_map::Entry;
use std::hash::{Hash as _, Hasher as _};
use std::ops::Index;
use std::rc::Rc;
use std::sync::LazyLock;
use ruff_index::{FrozenIndexVec, Idx, IndexVec, newtype_index};
use ruff_text_size::TextRange;
use rustc_hash::{FxBuildHasher, FxHashMap, FxHasher};
use smallvec::SmallVec;
use thin_vec::ThinVec;
use crate::ast_ids::ScopedUseId;
use crate::definition::{Definition, DefinitionCategory, DefinitionState};
use crate::frozen::FrozenMap;
use crate::member::ScopedMemberId;
use crate::narrowing_constraints::{
ConstraintKey, NarrowingConstraints, NarrowingConstraintsBuilder, ScopedNarrowingConstraint,
};
use crate::place::{PlaceExprRef, ScopedPlaceId};
use crate::predicate::{PredicateOrLiteral, Predicates, PredicatesBuilder, ScopedPredicateId};
use crate::reachability_constraints::{
ReachabilityConstraints, ReachabilityConstraintsBuilder, ScopedReachabilityConstraintId,
};
use crate::scope::{FileScopeId, ScopeKind, ScopeLaziness};
use crate::symbol::ScopedSymbolId;
use crate::use_def::place_state::{
Bindings, Declarations, EnclosingSnapshot, LiveBindingsIterator, LiveDeclaration,
LiveDeclarationsIterator, PlaceState,
};
use crate::{
BoundnessAnalysis, EnclosingSnapshotResult, LoopHeader, PossiblyNarrowedPlaces, SemanticIndex,
};
mod exception_checkpoint;
mod place_state;
pub(super) use exception_checkpoint::ExceptionCheckpointKey;
use exception_checkpoint::{ExceptionCheckpointSnapshot, ExceptionCheckpointState};
pub use place_state::LiveBinding;
pub use place_state::ScopedDefinitionId;
pub(super) use place_state::{FutureDefinitions, PreviousDefinitions};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(super) enum LiveBindingStatus {
Unbound,
PossiblyBound,
Bound,
}
#[newtype_index]
#[derive(get_size2::GetSize)]
pub struct LoopHeaderId;
#[newtype_index]
#[derive(get_size2::GetSize, salsa::SalsaValue)]
struct InternedBindingsId;
#[newtype_index]
#[derive(get_size2::GetSize, salsa::SalsaValue)]
struct InternedDeclarationsId;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
struct InternedPlaceStateId(InternedBindingsId, InternedDeclarationsId);
impl InternedPlaceStateId {
fn bindings_id(self) -> InternedBindingsId {
self.0
}
fn declarations_id(self) -> InternedDeclarationsId {
self.1
}
}
struct PlaceStateInterner {
interned_bindings: RetainedBindingsBuilder,
interned_ids_by_bindings: hashbrown::HashTable<InternedBindingsId>,
interned_declarations: RetainedDeclarationsBuilder,
interned_ids_by_declarations: FxHashMap<Declarations, InternedDeclarationsId>,
undeclared_declarations_by_constraint:
IndexVec<ScopedReachabilityConstraintId, Option<InternedDeclarationsId>>,
always_unbound_bindings: Option<InternedBindingsId>,
always_undeclared_declarations: Option<InternedDeclarationsId>,
}
impl PlaceStateInterner {
fn with_capacity(bindings: usize, declaration_map: usize, declarations: usize) -> Self {
Self {
interned_bindings: RetainedBindingsBuilder::with_capacity(bindings),
interned_ids_by_bindings: hashbrown::HashTable::with_capacity(bindings),
interned_declarations: RetainedDeclarationsBuilder::with_capacity(declarations),
interned_ids_by_declarations: FxHashMap::with_capacity_and_hasher(
declaration_map,
FxBuildHasher,
),
undeclared_declarations_by_constraint: IndexVec::new(),
always_unbound_bindings: None,
always_undeclared_declarations: None,
}
}
fn intern_bindings(&mut self, bindings: &Bindings) -> InternedBindingsId {
if bindings.is_always_unbound() {
if let Some(interned_id) = self.always_unbound_bindings {
return interned_id;
}
let interned_id = self.interned_bindings.push(bindings);
self.always_unbound_bindings = Some(interned_id);
return interned_id;
}
let hash = Self::hash_bindings(bindings.as_slice());
let interned_bindings = &mut self.interned_bindings;
let entry = self.interned_ids_by_bindings.entry(
hash,
|id| interned_bindings.get(*id) == bindings.as_slice(),
|id| Self::hash_bindings(interned_bindings.get(*id)),
);
match entry {
hashbrown::hash_table::Entry::Occupied(entry) => *entry.get(),
hashbrown::hash_table::Entry::Vacant(entry) => {
let interned_id = interned_bindings.push(bindings);
entry.insert(interned_id);
interned_id
}
}
}
fn hash_bindings(live_bindings: &[LiveBinding]) -> u64 {
let mut hasher = FxHasher::default();
live_bindings.hash(&mut hasher);
hasher.finish()
}
fn intern_declarations(&mut self, declarations: Declarations) -> InternedDeclarationsId {
if declarations.is_always_undeclared() {
if let Some(interned_id) = self.always_undeclared_declarations {
return interned_id;
}
let interned_id = self.interned_declarations.push(&declarations);
self.always_undeclared_declarations = Some(interned_id);
return interned_id;
}
if let Some(reachability_constraint) = declarations.undeclared_reachability_constraint()
&& !reachability_constraint.is_terminal()
{
let index = reachability_constraint.index();
let len = self.undeclared_declarations_by_constraint.len();
if index >= len {
self.undeclared_declarations_by_constraint
.resize(index + 1, None);
} else if let Some(interned_id) =
self.undeclared_declarations_by_constraint[reachability_constraint]
{
return interned_id;
}
let interned_id = self.interned_declarations.push(&declarations);
self.undeclared_declarations_by_constraint[reachability_constraint] = Some(interned_id);
return interned_id;
}
match self.interned_ids_by_declarations.entry(declarations) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let interned_id = self.interned_declarations.push(entry.key());
entry.insert(interned_id);
interned_id
}
}
}
fn intern_place_state(
&mut self,
bindings: &Bindings,
declarations: Declarations,
) -> InternedPlaceStateId {
InternedPlaceStateId(
self.intern_bindings(bindings),
self.intern_declarations(declarations),
)
}
fn retain_place_state(
&mut self,
bindings: &Bindings,
declarations: Declarations,
) -> InternedPlaceStateId {
let declarations_id = if declarations.undeclared_reachability_constraint().is_some() {
self.intern_declarations(declarations)
} else {
self.interned_declarations.push(&declarations)
};
InternedPlaceStateId(self.intern_bindings(bindings), declarations_id)
}
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize)]
struct RetainedBindings {
ends: FrozenIndexVec<InternedBindingsId, u32>,
live_bindings: Box<[LiveBinding]>,
}
struct RetainedBindingsBuilder {
ends: IndexVec<InternedBindingsId, u32>,
live_bindings: Vec<LiveBinding>,
}
impl RetainedBindingsBuilder {
fn with_capacity(bindings: usize) -> Self {
Self {
ends: IndexVec::with_capacity(bindings),
live_bindings: Vec::with_capacity(bindings),
}
}
fn push(&mut self, bindings: &Bindings) -> InternedBindingsId {
self.live_bindings.extend_from_slice(bindings.as_slice());
let end = u32::try_from(self.live_bindings.len())
.expect("Expected live-bindings length to fit into a u32");
self.ends.push(end)
}
fn get(&self, index: InternedBindingsId) -> &[LiveBinding] {
let end = self.ends[index];
let start = if index.index() == 0 {
0
} else {
self.ends[InternedBindingsId::new(index.index() - 1)]
};
&self.live_bindings[start as usize..end as usize]
}
fn finish(
self,
narrowing_constraints: &mut NarrowingConstraintsBuilder,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> RetainedBindings {
for binding in &self.live_bindings {
reachability_constraints.mark_used(binding.reachability_constraint());
narrowing_constraints.mark_used(binding.narrowing_constraint());
}
RetainedBindings {
ends: self.ends.into(),
live_bindings: self.live_bindings.into_boxed_slice(),
}
}
}
impl Index<InternedBindingsId> for RetainedBindings {
type Output = [LiveBinding];
fn index(&self, index: InternedBindingsId) -> &Self::Output {
let end = self.ends[index];
let start = if index.index() == 0 {
0
} else {
self.ends[InternedBindingsId::new(index.index() - 1)]
};
&self.live_bindings[start as usize..end as usize]
}
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize)]
struct RetainedDeclarations {
ends: FrozenIndexVec<InternedDeclarationsId, u32>,
live_declarations: Box<[LiveDeclaration]>,
}
struct RetainedDeclarationsBuilder {
ends: IndexVec<InternedDeclarationsId, u32>,
live_declarations: Vec<LiveDeclaration>,
}
impl RetainedDeclarationsBuilder {
fn with_capacity(declarations: usize) -> Self {
Self {
ends: IndexVec::with_capacity(declarations),
live_declarations: Vec::with_capacity(declarations),
}
}
fn push(&mut self, declarations: &Declarations) -> InternedDeclarationsId {
self.live_declarations.extend(declarations.iter().cloned());
let end = u32::try_from(self.live_declarations.len())
.expect("Expected live-declarations length to fit into a u32");
self.ends.push(end)
}
fn finish(
self,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> RetainedDeclarations {
for declaration in &self.live_declarations {
reachability_constraints.mark_used(declaration.reachability_constraint);
}
RetainedDeclarations {
ends: self.ends.into(),
live_declarations: self.live_declarations.into_boxed_slice(),
}
}
}
impl Index<InternedDeclarationsId> for RetainedDeclarations {
type Output = [LiveDeclaration];
fn index(&self, index: InternedDeclarationsId) -> &Self::Output {
let end = self.ends[index];
let start = if index.index() == 0 {
0
} else {
self.ends[InternedDeclarationsId::new(index.index() - 1)]
};
&self.live_declarations[start as usize..end as usize]
}
}
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
struct RetainedPlaceStates<T> {
end_of_scope: T,
reachable: T,
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize, salsa::SalsaValue)]
struct DefinitionsAtDefinition<B, D> {
bindings: B,
declarations: Option<D>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
enum InternedEnclosingSnapshotId {
Constraint(ScopedNarrowingConstraint),
Bindings(InternedBindingsId),
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize, salsa::SalsaValue)]
struct ConstraintTables<'db> {
predicates: Predicates<'db>,
predicate_narrowing_targets: PredicateNarrowingTargets,
reachability_constraints: ReachabilityConstraints,
narrowing_constraints: NarrowingConstraints,
}
#[derive(Debug, Default, PartialEq, Eq, get_size2::GetSize, salsa::SalsaValue)]
pub struct PredicateNarrowingTargets(Box<[(ScopedPredicateId, ScopedPlaceId)]>);
impl PredicateNarrowingTargets {
fn from_entries(mut entries: Vec<(ScopedPredicateId, ScopedPlaceId)>) -> Self {
entries.sort_unstable_by_key(|&(predicate, place)| (place, predicate));
entries.dedup();
Self(entries.into_boxed_slice())
}
pub fn contains(&self, predicate: ScopedPredicateId, place: ScopedPlaceId) -> bool {
self.0
.binary_search_by_key(&(place, predicate), |&(predicate, place)| {
(place, predicate)
})
.is_ok()
}
pub fn contains_place(&self, place: ScopedPlaceId) -> bool {
self.0
.binary_search_by_key(&place, |&(_, target)| target)
.is_ok()
}
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize)]
struct UseDefMapExtra {
bindings_by_use: FrozenIndexVec<ScopedUseId, InternedBindingsId>,
multi_bindings_by_use: MultiBindingsByUse,
member_states: FrozenIndexVec<ScopedMemberId, RetainedPlaceStates<InternedPlaceStateId>>,
enclosing_snapshots: FrozenIndexVec<ScopedEnclosingSnapshotId, InternedEnclosingSnapshotId>,
loop_headers: FrozenIndexVec<LoopHeaderId, LoopHeader>,
}
static EMPTY_CONSTRAINT_TABLES: LazyLock<ConstraintTables<'static>> =
LazyLock::new(|| ConstraintTables {
predicates: IndexVec::new().into(),
predicate_narrowing_targets: PredicateNarrowingTargets::default(),
reachability_constraints: ReachabilityConstraintsBuilder::default().build(),
narrowing_constraints: NarrowingConstraintsBuilder::default().build(),
});
static ALWAYS_UNBOUND_BINDINGS: LazyLock<Bindings> =
LazyLock::new(|| Bindings::unbound(ScopedReachabilityConstraintId::ALWAYS_TRUE));
static ALWAYS_UNDECLARED_DECLARATIONS: LazyLock<Declarations> =
LazyLock::new(|| Declarations::undeclared(ScopedReachabilityConstraintId::ALWAYS_TRUE));
#[derive(Clone, Copy, Debug, PartialEq, Eq, get_size2::GetSize, salsa::SalsaValue)]
enum DefinitionEntry<'db> {
DeclarationPart(Definition<'db>),
Unused(Definition<'db>),
Used(Definition<'db>),
Undefined,
Deleted,
}
impl<'db> DefinitionEntry<'db> {
fn state(self) -> DefinitionState<'db> {
match self {
Self::DeclarationPart(definition)
| Self::Unused(definition)
| Self::Used(definition) => DefinitionState::Defined(definition),
Self::Undefined => DefinitionState::Undefined,
Self::Deleted => DefinitionState::Deleted,
}
}
}
static_assertions::assert_eq_size!(DefinitionEntry<'static>, DefinitionState<'static>);
#[derive(Debug, PartialEq, Eq, get_size2::GetSize, salsa::SalsaValue)]
struct RetainedDefinitions<'db> {
states: Box<[DefinitionEntry<'db>]>,
}
impl<'db> RetainedDefinitions<'db> {
fn new(states: IndexVec<ScopedDefinitionId, DefinitionEntry<'db>>) -> Self {
let mut states = states.into_iter();
let unbound_state = states.next();
debug_assert_eq!(unbound_state, Some(DefinitionEntry::Undefined));
Self {
states: states.collect(),
}
}
#[inline]
fn get(&self, id: ScopedDefinitionId) -> DefinitionEntry<'db> {
let index = id.index();
if index == 0 {
DefinitionEntry::Undefined
} else {
self.states[index - 1]
}
}
fn iter_enumerated(
&self,
) -> impl Iterator<Item = (ScopedDefinitionId, DefinitionEntry<'db>)> + '_ {
self.states
.iter()
.copied()
.enumerate()
.map(|(index, entry)| (ScopedDefinitionId::new(index + 1), entry))
}
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize, salsa::SalsaValue)]
pub struct UseDefMap<'db> {
all_definitions: RetainedDefinitions<'db>,
constraint_tables: Option<Box<ConstraintTables<'db>>>,
interned_bindings: RetainedBindings,
interned_declarations: RetainedDeclarations,
range_reachability: Box<[(TextRange, RangeInfo)]>,
definitions_by_definition: FrozenMap<
Definition<'db>,
DefinitionsAtDefinition<InternedBindingsId, InternedDeclarationsId>,
>,
symbol_states: FrozenIndexVec<ScopedSymbolId, RetainedPlaceStates<InternedPlaceStateId>>,
extra: Option<Box<UseDefMapExtra>>,
end_of_scope_reachability: ScopedReachabilityConstraintId,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, get_size2::GetSize)]
struct RangeInfo {
reachability: ScopedReachabilityConstraintId,
in_type_checking_block: bool,
}
impl Default for RangeInfo {
fn default() -> Self {
Self {
reachability: ScopedReachabilityConstraintId::ALWAYS_TRUE,
in_type_checking_block: false,
}
}
}
#[derive(Debug, PartialEq, Eq, get_size2::GetSize)]
struct MultiBindingsByUse(ThinVec<(ScopedUseId, Box<[Bindings]>)>);
impl MultiBindingsByUse {
fn from_map(map: FxHashMap<ScopedUseId, Vec<Bindings>>) -> Self {
let mut entries = map
.into_iter()
.map(|(use_id, bindings)| (use_id, bindings.into_boxed_slice()))
.collect::<Vec<_>>();
entries.sort_unstable_by_key(|(use_id, _)| *use_id);
Self(entries.into_iter().collect())
}
fn get(&self, use_id: ScopedUseId) -> Option<&[Bindings]> {
self.0
.binary_search_by_key(&use_id, |(candidate, _)| *candidate)
.ok()
.map(|index| self.0[index].1.as_ref())
}
}
pub enum ApplicableConstraints<'map, 'db> {
UnboundBinding(NarrowingEvaluator<'map, 'db>),
ConstrainedBindings(BindingWithConstraintsIterator<'map, 'db>),
}
impl<'db> UseDefMap<'db> {
fn constraint_tables(&self) -> &ConstraintTables<'db> {
self.constraint_tables
.as_deref()
.map_or(&EMPTY_CONSTRAINT_TABLES, |tables| tables)
}
fn extra(&self) -> &UseDefMapExtra {
self.extra
.as_deref()
.expect("extra use-def data should have been retained")
}
pub fn loop_header(&self, id: LoopHeaderId) -> &LoopHeader {
&self.extra().loop_headers[id]
}
pub fn reachability_constraints(&self) -> &ReachabilityConstraints {
&self.constraint_tables().reachability_constraints
}
pub fn predicates(&self) -> &Predicates<'db> {
&self.constraint_tables().predicates
}
pub fn range_reachability(
&self,
) -> impl Iterator<Item = (TextRange, ScopedReachabilityConstraintId)> + '_ {
self.range_reachability
.iter()
.map(|&(range, RangeInfo { reachability, .. })| (range, reachability))
}
pub fn end_of_scope_reachability(&self) -> ScopedReachabilityConstraintId {
self.end_of_scope_reachability
}
pub fn definitions_with_usage(
&self,
) -> impl Iterator<Item = (ScopedDefinitionId, Definition<'db>, bool)> + '_ {
self.all_definitions
.iter_enumerated()
.filter_map(|(id, entry)| match entry {
DefinitionEntry::Unused(definition) => Some((id, definition, false)),
DefinitionEntry::Used(definition) => Some((id, definition, true)),
DefinitionEntry::DeclarationPart(_)
| DefinitionEntry::Undefined
| DefinitionEntry::Deleted => None,
})
}
pub fn bindings_at_use(&self, use_id: ScopedUseId) -> BindingWithConstraintsIterator<'_, 'db> {
let bindings_id = self.extra().bindings_by_use[use_id];
self.bindings_iterator(
&self.interned_bindings[bindings_id],
BoundnessAnalysis::BasedOnUnboundVisibility,
)
}
pub fn multi_bindings_at_use(
&self,
use_id: ScopedUseId,
) -> impl Iterator<Item = BindingWithConstraintsIterator<'_, 'db>> {
self.extra
.as_deref()
.and_then(|extra| extra.multi_bindings_by_use.get(use_id))
.map(|member_bindings| {
member_bindings.iter().map(|bindings| {
self.bindings_iterator(
bindings.as_slice(),
BoundnessAnalysis::BasedOnUnboundVisibility,
)
})
})
.into_iter()
.flatten()
}
pub fn applicable_constraints(
&self,
constraint_key: ConstraintKey,
enclosing_scope: FileScopeId,
expr: PlaceExprRef,
index: &'db SemanticIndex,
) -> ApplicableConstraints<'_, 'db> {
match constraint_key {
ConstraintKey::NarrowingConstraint(constraint) => {
ApplicableConstraints::UnboundBinding(NarrowingEvaluator {
constraint,
constraint_tables: self.constraint_tables(),
})
}
ConstraintKey::NestedScope(nested_scope) => {
let EnclosingSnapshotResult::FoundBindings(bindings) =
index.enclosing_snapshot(enclosing_scope, expr, nested_scope)
else {
unreachable!(
"The result of `SemanticIndex::eager_snapshot` must be `FoundBindings`"
)
};
ApplicableConstraints::ConstrainedBindings(bindings)
}
ConstraintKey::UseId(use_id) => {
ApplicableConstraints::ConstrainedBindings(self.bindings_at_use(use_id))
}
}
}
pub fn definition(&self, id: ScopedDefinitionId) -> DefinitionState<'db> {
self.all_definitions.get(id).state()
}
pub fn narrowing_evaluator(
&self,
constraint: ScopedNarrowingConstraint,
) -> NarrowingEvaluator<'_, 'db> {
NarrowingEvaluator {
constraint,
constraint_tables: self.constraint_tables(),
}
}
pub(crate) fn is_range_in_type_checking_block(&self, range: TextRange) -> bool {
self.range_reachability
.iter()
.take_while(|(entry_range, _)| entry_range.start() <= range.start())
.any(|&(entry_range, block)| {
block.in_type_checking_block && entry_range.contains_range(range)
})
}
pub fn end_of_scope_bindings(
&self,
place: ScopedPlaceId,
) -> BindingWithConstraintsIterator<'_, 'db> {
match place {
ScopedPlaceId::Symbol(symbol) => self.end_of_scope_symbol_bindings(symbol),
ScopedPlaceId::Member(member) => self.end_of_scope_member_bindings(member),
}
}
pub fn end_of_scope_symbol_bindings(
&self,
symbol: ScopedSymbolId,
) -> BindingWithConstraintsIterator<'_, 'db> {
let place_state_id = self.symbol_states[symbol].end_of_scope;
self.bindings_iterator(
&self.interned_bindings[place_state_id.bindings_id()],
BoundnessAnalysis::BasedOnUnboundVisibility,
)
}
fn end_of_scope_member_bindings(
&self,
member: ScopedMemberId,
) -> BindingWithConstraintsIterator<'_, 'db> {
let place_state_id = self.extra().member_states[member].end_of_scope;
self.bindings_iterator(
&self.interned_bindings[place_state_id.bindings_id()],
BoundnessAnalysis::BasedOnUnboundVisibility,
)
}
pub fn reachable_bindings(
&self,
place: ScopedPlaceId,
) -> BindingWithConstraintsIterator<'_, 'db> {
match place {
ScopedPlaceId::Symbol(symbol) => self.reachable_symbol_bindings(symbol),
ScopedPlaceId::Member(member) => self.reachable_member_bindings(member),
}
}
pub fn reachable_symbol_bindings(
&self,
symbol: ScopedSymbolId,
) -> BindingWithConstraintsIterator<'_, 'db> {
let place_state_id = self.symbol_states[symbol].reachable;
let bindings = &self.interned_bindings[place_state_id.bindings_id()];
self.bindings_iterator(bindings, BoundnessAnalysis::AssumeBound)
}
pub fn reachable_member_bindings(
&self,
member: ScopedMemberId,
) -> BindingWithConstraintsIterator<'_, 'db> {
let place_state_id = self.extra().member_states[member].reachable;
let bindings = &self.interned_bindings[place_state_id.bindings_id()];
self.bindings_iterator(bindings, BoundnessAnalysis::AssumeBound)
}
pub(crate) fn enclosing_snapshot(
&self,
snapshot_id: ScopedEnclosingSnapshotId,
nested_laziness: ScopeLaziness,
) -> EnclosingSnapshotResult<'_, 'db> {
let boundness_analysis = if nested_laziness.is_eager() {
BoundnessAnalysis::BasedOnUnboundVisibility
} else {
BoundnessAnalysis::AssumeBound
};
let Some(extra) = self.extra.as_deref() else {
return EnclosingSnapshotResult::NotFound;
};
match extra.enclosing_snapshots.get(snapshot_id) {
Some(InternedEnclosingSnapshotId::Constraint(constraint)) => {
EnclosingSnapshotResult::FoundConstraint(*constraint)
}
Some(InternedEnclosingSnapshotId::Bindings(bindings_id)) => {
EnclosingSnapshotResult::FoundBindings(
self.bindings_iterator(
&self.interned_bindings[*bindings_id],
boundness_analysis,
),
)
}
None => EnclosingSnapshotResult::NotFound,
}
}
pub fn bindings_at_definition(
&self,
definition: Definition<'db>,
) -> BindingWithConstraintsIterator<'_, 'db> {
let bindings = self.definitions_by_definition.get(&definition).map_or_else(
|| ALWAYS_UNBOUND_BINDINGS.as_slice(),
|definitions| &self.interned_bindings[definitions.bindings],
);
self.bindings_iterator(bindings, BoundnessAnalysis::BasedOnUnboundVisibility)
}
pub fn declarations_at_binding(
&self,
binding: Definition<'db>,
) -> DeclarationsIterator<'_, 'db> {
let declarations = self.definitions_by_definition.get(&binding).map_or_else(
|| ALWAYS_UNDECLARED_DECLARATIONS.as_slice(),
|definitions| {
&self.interned_declarations[definitions
.declarations
.expect("binding definition should have retained declarations")]
},
);
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
}
pub fn end_of_scope_declarations<'map>(
&'map self,
place: ScopedPlaceId,
) -> DeclarationsIterator<'map, 'db> {
match place {
ScopedPlaceId::Symbol(symbol) => self.end_of_scope_symbol_declarations(symbol),
ScopedPlaceId::Member(member) => self.end_of_scope_member_declarations(member),
}
}
pub fn end_of_scope_symbol_declarations<'map>(
&'map self,
symbol: ScopedSymbolId,
) -> DeclarationsIterator<'map, 'db> {
let place_state_id = self.symbol_states[symbol].end_of_scope;
let declarations = &self.interned_declarations[place_state_id.declarations_id()];
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
}
fn end_of_scope_member_declarations<'map>(
&'map self,
member: ScopedMemberId,
) -> DeclarationsIterator<'map, 'db> {
let place_state_id = self.extra().member_states[member].end_of_scope;
let declarations = &self.interned_declarations[place_state_id.declarations_id()];
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
}
pub fn reachable_symbol_declarations(
&self,
symbol: ScopedSymbolId,
) -> DeclarationsIterator<'_, 'db> {
let place_state_id = self.symbol_states[symbol].reachable;
let declarations = &self.interned_declarations[place_state_id.declarations_id()];
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
}
pub fn reachable_member_declarations(
&self,
member: ScopedMemberId,
) -> DeclarationsIterator<'_, 'db> {
let place_state_id = self.extra().member_states[member].reachable;
let declarations = &self.interned_declarations[place_state_id.declarations_id()];
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
}
pub fn reachable_declarations(&self, place: ScopedPlaceId) -> DeclarationsIterator<'_, 'db> {
match place {
ScopedPlaceId::Symbol(symbol) => self.reachable_symbol_declarations(symbol),
ScopedPlaceId::Member(member) => self.reachable_member_declarations(member),
}
}
pub fn all_end_of_scope_symbol_declarations<'map>(
&'map self,
) -> impl Iterator<Item = (ScopedSymbolId, DeclarationsIterator<'map, 'db>)> + 'map {
self.symbol_states
.indices()
.map(|symbol_id| (symbol_id, self.end_of_scope_symbol_declarations(symbol_id)))
}
pub fn all_end_of_scope_symbol_bindings<'map>(
&'map self,
) -> impl Iterator<Item = (ScopedSymbolId, BindingWithConstraintsIterator<'map, 'db>)> + 'map
{
self.symbol_states
.indices()
.map(|symbol_id| (symbol_id, self.end_of_scope_symbol_bindings(symbol_id)))
}
pub fn all_reachable_symbols<'map>(
&'map self,
) -> impl Iterator<
Item = (
ScopedSymbolId,
DeclarationsIterator<'map, 'db>,
BindingWithConstraintsIterator<'map, 'db>,
),
> + 'map {
self.symbol_states.iter_enumerated().map(
|(symbol_id, RetainedPlaceStates { reachable, .. })| {
let declarations = self.declarations_iterator(
&self.interned_declarations[reachable.declarations_id()],
BoundnessAnalysis::AssumeBound,
);
let bindings = self.bindings_iterator(
&self.interned_bindings[reachable.bindings_id()],
BoundnessAnalysis::AssumeBound,
);
(symbol_id, declarations, bindings)
},
)
}
fn bindings_iterator<'map>(
&'map self,
bindings: &'map [LiveBinding],
boundness_analysis: BoundnessAnalysis,
) -> BindingWithConstraintsIterator<'map, 'db> {
BindingWithConstraintsIterator {
all_definitions: &self.all_definitions,
constraint_tables: self.constraint_tables(),
boundness_analysis,
inner: bindings.iter(),
}
}
fn declarations_iterator<'map>(
&'map self,
declarations: &'map [LiveDeclaration],
boundness_analysis: BoundnessAnalysis,
) -> DeclarationsIterator<'map, 'db> {
DeclarationsIterator {
all_definitions: &self.all_definitions,
constraint_tables: self.constraint_tables(),
boundness_analysis,
inner: declarations.iter(),
}
}
}
#[newtype_index]
#[derive(get_size2::GetSize)]
pub(crate) struct ScopedEnclosingSnapshotId;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, get_size2::GetSize)]
pub(crate) struct EnclosingSnapshotKey {
pub(crate) enclosing_scope: FileScopeId,
pub(crate) enclosing_place: ScopedPlaceId,
pub(crate) nested_scope: FileScopeId,
pub(crate) nested_laziness: ScopeLaziness,
}
type EnclosingSnapshots = IndexVec<ScopedEnclosingSnapshotId, EnclosingSnapshot>;
#[derive(Clone, Debug)]
pub struct BindingWithConstraintsIterator<'map, 'db> {
all_definitions: &'map RetainedDefinitions<'db>,
constraint_tables: &'map ConstraintTables<'db>,
boundness_analysis: BoundnessAnalysis,
inner: LiveBindingsIterator<'map>,
}
impl<'map, 'db> BindingWithConstraintsIterator<'map, 'db> {
pub const fn predicates(&self) -> &'map Predicates<'db> {
&self.constraint_tables.predicates
}
pub const fn reachability_constraints(&self) -> &'map ReachabilityConstraints {
&self.constraint_tables.reachability_constraints
}
pub const fn boundness_analysis(&self) -> BoundnessAnalysis {
self.boundness_analysis
}
}
impl<'map, 'db> Iterator for BindingWithConstraintsIterator<'map, 'db> {
type Item = BindingWithConstraints<'map, 'db>;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|live_binding| BindingWithConstraints {
binding: self.all_definitions.get(live_binding.binding()).state(),
binding_order: live_binding.binding(),
narrowing_constraint: NarrowingEvaluator {
constraint: live_binding.narrowing_constraint(),
constraint_tables: self.constraint_tables,
},
reachability_constraint: live_binding.reachability_constraint(),
})
}
}
impl std::iter::FusedIterator for BindingWithConstraintsIterator<'_, '_> {}
pub struct BindingWithConstraints<'map, 'db> {
pub binding: DefinitionState<'db>,
pub binding_order: ScopedDefinitionId,
pub narrowing_constraint: NarrowingEvaluator<'map, 'db>,
pub reachability_constraint: ScopedReachabilityConstraintId,
}
pub struct NarrowingEvaluator<'map, 'db> {
constraint: ScopedNarrowingConstraint,
constraint_tables: &'map ConstraintTables<'db>,
}
impl<'map, 'db> NarrowingEvaluator<'map, 'db> {
pub fn constraint(&self) -> ScopedNarrowingConstraint {
self.constraint
}
pub fn predicates(&self) -> &'map Predicates<'db> {
&self.constraint_tables.predicates
}
pub fn predicate_narrowing_targets(&self) -> &'map PredicateNarrowingTargets {
&self.constraint_tables.predicate_narrowing_targets
}
pub fn narrowing_constraints(&self) -> &'map NarrowingConstraints {
&self.constraint_tables.narrowing_constraints
}
}
#[derive(Clone)]
pub struct DeclarationsIterator<'map, 'db> {
all_definitions: &'map RetainedDefinitions<'db>,
constraint_tables: &'map ConstraintTables<'db>,
boundness_analysis: BoundnessAnalysis,
inner: LiveDeclarationsIterator<'map>,
}
impl<'map, 'db> DeclarationsIterator<'map, 'db> {
pub const fn predicates(&self) -> &'map Predicates<'db> {
&self.constraint_tables.predicates
}
pub const fn reachability_constraints(&self) -> &'map ReachabilityConstraints {
&self.constraint_tables.reachability_constraints
}
pub const fn boundness_analysis(&self) -> BoundnessAnalysis {
self.boundness_analysis
}
}
#[derive(Debug, Clone)]
pub struct DeclarationWithConstraint<'db> {
pub declaration: DefinitionState<'db>,
pub declaration_order: ScopedDefinitionId,
pub reachability_constraint: ScopedReachabilityConstraintId,
}
impl<'db> Iterator for DeclarationsIterator<'_, 'db> {
type Item = DeclarationWithConstraint<'db>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(
|LiveDeclaration {
declaration,
reachability_constraint,
}| {
DeclarationWithConstraint {
declaration: self.all_definitions.get(*declaration).state(),
declaration_order: *declaration,
reachability_constraint: *reachability_constraint,
}
},
)
}
}
impl std::iter::FusedIterator for DeclarationsIterator<'_, '_> {}
#[derive(Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
struct ReachableDefinitions {
bindings: Bindings,
declarations: Declarations,
}
#[derive(Clone, Debug)]
pub(super) struct FlowSnapshot {
symbol_states: IndexVec<ScopedSymbolId, PendingPlaceState>,
member_states: IndexVec<ScopedMemberId, PendingPlaceState>,
reachability: ScopedReachabilityConstraintId,
checkpoint_flow: ScopedReachabilityConstraintId,
checkpoint_state: ExceptionCheckpointSnapshot,
pending_reachability: PendingReachabilityId,
}
impl FlowSnapshot {
pub(super) fn is_always_unreachable(&self) -> bool {
self.reachability == ScopedReachabilityConstraintId::ALWAYS_FALSE
}
}
#[newtype_index]
struct PendingReachabilityId;
#[derive(Debug)]
struct PendingReachabilityConstraint {
parent: PendingReachabilityId,
reachability_constraint: ScopedReachabilityConstraintId,
narrowing_constraint: ScopedNarrowingConstraint,
}
#[derive(Debug)]
struct PendingReachability {
constraints: IndexVec<PendingReachabilityId, PendingReachabilityConstraint>,
current: PendingReachabilityId,
}
impl Default for PendingReachability {
fn default() -> Self {
let mut constraints = IndexVec::new();
let root = constraints.next_index();
constraints.push(PendingReachabilityConstraint {
parent: root,
reachability_constraint: ScopedReachabilityConstraintId::ALWAYS_TRUE,
narrowing_constraint: ScopedNarrowingConstraint::ALWAYS_TRUE,
});
Self {
constraints,
current: root,
}
}
}
impl PendingReachability {
fn push(
&mut self,
reachability_constraint: ScopedReachabilityConstraintId,
narrowing_constraint: ScopedNarrowingConstraint,
) {
self.current = self.constraints.push(PendingReachabilityConstraint {
parent: self.current,
reachability_constraint,
narrowing_constraint,
});
}
fn materialize<'a>(
&self,
pending: &'a mut PendingPlaceState,
target: PendingReachabilityId,
narrowing_constraints: &mut NarrowingConstraintsBuilder,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> &'a mut PlaceState {
self.materialize_reachability(pending, target, reachability_constraints);
self.materialize_narrowing(pending, target, narrowing_constraints);
Rc::make_mut(&mut pending.state)
}
fn materialize_narrowing(
&self,
pending: &mut PendingPlaceState,
target: PendingReachabilityId,
narrowing_constraints: &mut NarrowingConstraintsBuilder,
) {
if pending.narrowing != target {
let mut unapplied = SmallVec::<[ScopedNarrowingConstraint; 4]>::new();
let mut current = target;
while current != pending.narrowing {
let event = &self.constraints[current];
if event.narrowing_constraint != ScopedNarrowingConstraint::ALWAYS_TRUE {
unapplied.push(event.narrowing_constraint);
}
assert_ne!(
current, event.parent,
"pending narrowing must be an ancestor"
);
current = event.parent;
}
if !unapplied.is_empty() {
let state = Rc::make_mut(&mut pending.state);
for constraint in unapplied.into_iter().rev() {
state.record_narrowing_constraint(narrowing_constraints, constraint);
}
}
pending.narrowing = target;
}
}
fn materialize_reachability<'a>(
&self,
pending: &'a mut PendingPlaceState,
target: PendingReachabilityId,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> &'a mut PlaceState {
if pending.reachability != target {
let mut unapplied = SmallVec::<[ScopedReachabilityConstraintId; 4]>::new();
let mut current = target;
while current != pending.reachability {
let event = &self.constraints[current];
unapplied.push(event.reachability_constraint);
assert_ne!(
current, event.parent,
"pending reachability must be an ancestor"
);
current = event.parent;
}
let state = Rc::make_mut(&mut pending.state);
for constraint in unapplied.into_iter().rev() {
state.record_reachability_constraint(reachability_constraints, constraint);
}
pending.reachability = target;
}
Rc::make_mut(&mut pending.state)
}
fn materialize_ref<'a>(
&self,
pending: &'a mut PendingPlaceState,
target: PendingReachabilityId,
narrowing_constraints: &mut NarrowingConstraintsBuilder,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> &'a PlaceState {
if pending.reachability != target || pending.narrowing != target {
self.materialize(
pending,
target,
narrowing_constraints,
reachability_constraints,
);
}
&pending.state
}
fn materialize_ref_at_use<'a>(
&self,
pending: &'a mut PendingPlaceState,
target: PendingReachabilityId,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> &'a PlaceState {
self.materialize_reachability(pending, target, reachability_constraints);
&pending.state
}
fn constraint_between(
&self,
ancestor: PendingReachabilityId,
target: PendingReachabilityId,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) -> ScopedReachabilityConstraintId {
let mut constraint = ScopedReachabilityConstraintId::ALWAYS_TRUE;
let mut current = target;
while current != ancestor {
let event = &self.constraints[current];
constraint = reachability_constraints
.add_and_constraint(constraint, event.reachability_constraint);
assert_ne!(
current, event.parent,
"pending reachability must be an ancestor"
);
current = event.parent;
}
constraint
}
fn narrowing_constraint_between(
&self,
ancestor: PendingReachabilityId,
target: PendingReachabilityId,
narrowing_constraints: &mut NarrowingConstraintsBuilder,
) -> ScopedNarrowingConstraint {
let mut unapplied = SmallVec::<[ScopedNarrowingConstraint; 4]>::new();
let mut current = target;
while current != ancestor {
let event = &self.constraints[current];
if event.narrowing_constraint != ScopedNarrowingConstraint::ALWAYS_TRUE {
unapplied.push(event.narrowing_constraint);
}
assert_ne!(
current, event.parent,
"pending narrowing must be an ancestor"
);
current = event.parent;
}
let mut constraint = ScopedNarrowingConstraint::ALWAYS_TRUE;
for pending in unapplied.into_iter().rev() {
constraint = narrowing_constraints.add_and_constraint(constraint, pending);
}
constraint
}
fn common_ancestor(
&self,
mut left: PendingReachabilityId,
mut right: PendingReachabilityId,
) -> PendingReachabilityId {
while left != right {
if left.index() > right.index() {
left = self.constraints[left].parent;
} else {
right = self.constraints[right].parent;
}
}
left
}
}
#[derive(Clone, Debug)]
struct PendingPlaceState {
state: Rc<PlaceState>,
reachability: PendingReachabilityId,
narrowing: PendingReachabilityId,
}
impl PendingPlaceState {
fn new(state: PlaceState, reachability: PendingReachabilityId) -> Self {
Self {
state: Rc::new(state),
reachability,
narrowing: reachability,
}
}
}
fn pending_place_state_mut<'a>(
place: ScopedPlaceId,
symbol_states: &'a mut IndexVec<ScopedSymbolId, PendingPlaceState>,
member_states: &'a mut IndexVec<ScopedMemberId, PendingPlaceState>,
) -> &'a mut PendingPlaceState {
match place {
ScopedPlaceId::Symbol(symbol) => &mut symbol_states[symbol],
ScopedPlaceId::Member(member) => &mut member_states[member],
}
}
impl PendingReachability {
fn merge_place_states<I: Idx>(
&self,
current_states: &mut IndexVec<I, PendingPlaceState>,
branch_states: IndexVec<I, PendingPlaceState>,
branch: PendingReachabilityId,
branch_reachability: ScopedReachabilityConstraintId,
narrowing_constraints: &mut NarrowingConstraintsBuilder,
reachability_constraints: &mut ReachabilityConstraintsBuilder,
) {
let branch_ancestor = self.common_ancestor(self.current, branch);
let current_narrowing =
self.narrowing_constraint_between(branch_ancestor, self.current, narrowing_constraints);
let branch_narrowing =
self.narrowing_constraint_between(branch_ancestor, branch, narrowing_constraints);
let merged_narrowing =
narrowing_constraints.add_or_constraint(current_narrowing, branch_narrowing);
let mut branch_states = branch_states.into_iter();
for current in current_states {
let Some(mut branch_state) = branch_states.next() else {
let current = self.materialize(
current,
self.current,
narrowing_constraints,
reachability_constraints,
);
current.merge(
PlaceState::undefined(branch_reachability),
narrowing_constraints,
reachability_constraints,
);
continue;
};
if current.reachability == branch_state.reachability
&& current.narrowing == branch_state.narrowing
&& Rc::ptr_eq(¤t.state, &branch_state.state)
{
if self.current == branch {
continue;
}
self.materialize_narrowing(current, branch_ancestor, narrowing_constraints);
if merged_narrowing != ScopedNarrowingConstraint::ALWAYS_TRUE {
Rc::make_mut(&mut current.state)
.record_narrowing_constraint(narrowing_constraints, merged_narrowing);
}
let current_constraint = self.constraint_between(
current.reachability,
self.current,
reachability_constraints,
);
let branch_constraint = self.constraint_between(
branch_state.reachability,
branch,
reachability_constraints,
);
let merged_constraint = reachability_constraints
.add_or_constraint(current_constraint, branch_constraint);
if merged_constraint != ScopedReachabilityConstraintId::ALWAYS_TRUE {
Rc::make_mut(&mut current.state).record_reachability_constraint(
reachability_constraints,
merged_constraint,
);
}
current.reachability = self.current;
current.narrowing = self.current;
continue;
}
self.materialize(
&mut branch_state,
branch,
narrowing_constraints,
reachability_constraints,
);
let branch_state = Rc::unwrap_or_clone(branch_state.state);
let current = self.materialize(
current,
self.current,
narrowing_constraints,
reachability_constraints,
);
current.merge(
branch_state,
narrowing_constraints,
reachability_constraints,
);
}
}
}
pub(super) struct SingleSymbolSnapshot {
symbol_state: PlaceState,
associated_member_states: FxHashMap<ScopedMemberId, PlaceState>,
}
#[derive(Debug)]
pub(super) struct UseDefMapBuilder<'db> {
all_definitions: IndexVec<ScopedDefinitionId, DefinitionEntry<'db>>,
predicates: PredicatesBuilder<'db>,
predicate_narrowing_targets: Vec<(ScopedPredicateId, ScopedPlaceId)>,
pub(super) reachability_constraints: ReachabilityConstraintsBuilder,
pub(super) narrowing_constraints: NarrowingConstraintsBuilder,
bindings_by_use: IndexVec<ScopedUseId, Bindings>,
multi_bindings_by_use: FxHashMap<ScopedUseId, Vec<Bindings>>,
pub(super) reachability: ScopedReachabilityConstraintId,
range_reachability: Vec<(TextRange, RangeInfo)>,
checkpoint_flow: ScopedReachabilityConstraintId,
checkpoint_state: ExceptionCheckpointState,
definitions_by_definition:
FxHashMap<Definition<'db>, DefinitionsAtDefinition<Bindings, Declarations>>,
symbol_states: IndexVec<ScopedSymbolId, PendingPlaceState>,
member_states: IndexVec<ScopedMemberId, PendingPlaceState>,
pending_reachability: PendingReachability,
reachable_symbol_definitions: IndexVec<ScopedSymbolId, ReachableDefinitions>,
reachable_member_definitions: IndexVec<ScopedMemberId, ReachableDefinitions>,
enclosing_snapshots: EnclosingSnapshots,
loop_headers: IndexVec<LoopHeaderId, LoopHeader>,
is_class_scope: bool,
reachability_narrowing_enabled: bool,
}
impl<'db> UseDefMapBuilder<'db> {
pub(super) fn new(scope_kind: ScopeKind) -> Self {
Self {
all_definitions: IndexVec::from_iter([DefinitionEntry::Undefined]),
predicates: PredicatesBuilder::default(),
predicate_narrowing_targets: Vec::new(),
reachability_constraints: ReachabilityConstraintsBuilder::default(),
narrowing_constraints: NarrowingConstraintsBuilder::default(),
bindings_by_use: IndexVec::new(),
multi_bindings_by_use: FxHashMap::default(),
reachability: ScopedReachabilityConstraintId::ALWAYS_TRUE,
range_reachability: Vec::new(),
checkpoint_flow: ScopedReachabilityConstraintId::ALWAYS_TRUE,
checkpoint_state: ExceptionCheckpointState::default(),
definitions_by_definition: FxHashMap::default(),
symbol_states: IndexVec::new(),
member_states: IndexVec::new(),
pending_reachability: PendingReachability::default(),
reachable_member_definitions: IndexVec::new(),
reachable_symbol_definitions: IndexVec::new(),
enclosing_snapshots: EnclosingSnapshots::default(),
loop_headers: IndexVec::new(),
is_class_scope: scope_kind.is_class(),
reachability_narrowing_enabled: matches!(
scope_kind,
ScopeKind::Module | ScopeKind::Class | ScopeKind::Function | ScopeKind::Lambda
),
}
}
pub(super) fn reserve_loop_header(&mut self) -> LoopHeaderId {
self.loop_headers.push(LoopHeader::new())
}
pub(super) fn set_loop_header(&mut self, id: LoopHeaderId, header: LoopHeader) {
self.loop_headers[id] = header;
}
fn push_definition(&mut self, entry: DefinitionEntry<'db>) -> ScopedDefinitionId {
self.checkpoint_state.record_binding_change();
self.all_definitions.push(entry)
}
pub(super) fn definition(&self, def_id: ScopedDefinitionId) -> DefinitionState<'db> {
self.all_definitions[def_id].state()
}
pub(super) fn mark_unreachable(&mut self) {
self.record_reachability_constraint(ScopedReachabilityConstraintId::ALWAYS_FALSE);
}
pub(super) fn add_place(&mut self, place: ScopedPlaceId) {
self.checkpoint_state.record_binding_change();
match place {
ScopedPlaceId::Symbol(symbol) => {
let new_place = self.symbol_states.push(PendingPlaceState::new(
PlaceState::undefined(self.reachability),
self.pending_reachability.current,
));
debug_assert_eq!(symbol, new_place);
let new_place = self
.reachable_symbol_definitions
.push(ReachableDefinitions {
bindings: Bindings::unbound(self.reachability),
declarations: Declarations::undeclared(self.reachability),
});
debug_assert_eq!(symbol, new_place);
}
ScopedPlaceId::Member(member) => {
let new_place = self.member_states.push(PendingPlaceState::new(
PlaceState::undefined(self.reachability),
self.pending_reachability.current,
));
debug_assert_eq!(member, new_place);
let new_place = self
.reachable_member_definitions
.push(ReachableDefinitions {
bindings: Bindings::unbound(self.reachability),
declarations: Declarations::undeclared(self.reachability),
});
debug_assert_eq!(member, new_place);
}
}
}
pub(super) fn next_definition_id(&self) -> ScopedDefinitionId {
self.all_definitions.next_index()
}
pub(super) fn exception_checkpoint_key(&self) -> ExceptionCheckpointKey {
self.checkpoint_state
.key((!self.reachability_constraints.is_saturated()).then_some(self.checkpoint_flow))
}
pub(super) fn record_binding(
&mut self,
place: ScopedPlaceId,
binding: Definition<'db>,
previous_definitions: PreviousDefinitions,
can_be_shadowed: FutureDefinitions,
) {
let pending = self.pending_reachability.current;
let def_id = self.push_definition(DefinitionEntry::Unused(binding));
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let place_state = self.pending_reachability.materialize(
place_state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
let definitions_at_definition = DefinitionsAtDefinition {
bindings: place_state.bindings().clone(),
declarations: Some(place_state.declarations().clone()),
};
place_state.record_binding(
def_id,
self.reachability,
self.is_class_scope,
place.is_symbol(),
previous_definitions,
can_be_shadowed,
);
self.definitions_by_definition
.insert(binding, definitions_at_definition);
let bindings = match place {
ScopedPlaceId::Symbol(symbol) => {
&mut self.reachable_symbol_definitions[symbol].bindings
}
ScopedPlaceId::Member(member) => {
&mut self.reachable_member_definitions[member].bindings
}
};
bindings.record_binding(
def_id,
self.reachability,
self.is_class_scope,
place.is_symbol(),
PreviousDefinitions::AreKept,
can_be_shadowed,
);
}
pub(crate) fn bindings_at_use(
&self,
use_id: ScopedUseId,
) -> impl Iterator<Item = &LiveBinding> {
self.bindings_by_use[use_id].iter()
}
pub(super) fn add_predicate(
&mut self,
predicate: PredicateOrLiteral<'db>,
) -> ScopedPredicateId {
match predicate {
PredicateOrLiteral::Predicate(predicate) => self.predicates.add_predicate(predicate),
PredicateOrLiteral::Literal(true) => ScopedPredicateId::ALWAYS_TRUE,
PredicateOrLiteral::Literal(false) => ScopedPredicateId::ALWAYS_FALSE,
}
}
pub(super) fn record_narrowing_constraint_for_places(
&mut self,
predicate: ScopedPredicateId,
places: &PossiblyNarrowedPlaces,
) {
if predicate == ScopedPredicateId::ALWAYS_TRUE
|| predicate == ScopedPredicateId::ALWAYS_FALSE
{
return;
}
self.predicate_narrowing_targets
.extend(places.iter().map(|place| (predicate, *place)));
let atom = self.narrowing_constraints.add_atom(predicate);
self.record_narrowing_constraint_node_for_places(atom, places);
}
pub(super) fn record_narrowing_constraint_for_bindings_at_use(
&mut self,
predicate: ScopedPredicateId,
place: ScopedPlaceId,
use_id: ScopedUseId,
) {
if predicate == ScopedPredicateId::ALWAYS_TRUE
|| predicate == ScopedPredicateId::ALWAYS_FALSE
{
return;
}
self.predicate_narrowing_targets.push((predicate, place));
let constraint = self.narrowing_constraints.add_atom(predicate);
let pending = self.pending_reachability.current;
let state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let state = self.pending_reachability.materialize(
state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
state.record_narrowing_constraint_for_bindings_at_use(
&mut self.narrowing_constraints,
constraint,
&self.bindings_by_use[use_id],
);
}
pub(super) fn record_narrowing_constraint_for_bindings(
&mut self,
predicate: ScopedPredicateId,
place: ScopedPlaceId,
bindings: &[ScopedDefinitionId],
) {
if predicate == ScopedPredicateId::ALWAYS_TRUE
|| predicate == ScopedPredicateId::ALWAYS_FALSE
{
return;
}
self.predicate_narrowing_targets.push((predicate, place));
let constraint = self.narrowing_constraints.add_atom(predicate);
let pending = self.pending_reachability.current;
let state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let state = self.pending_reachability.materialize(
state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
state.record_narrowing_constraint_for_bindings(
&mut self.narrowing_constraints,
constraint,
bindings,
);
}
pub(super) fn record_negated_narrowing_constraint_for_places(
&mut self,
predicate: ScopedPredicateId,
places: &PossiblyNarrowedPlaces,
) {
if predicate == ScopedPredicateId::ALWAYS_TRUE
|| predicate == ScopedPredicateId::ALWAYS_FALSE
{
return;
}
let negated = self.narrowing_constraints.add_negated_atom(predicate);
self.record_narrowing_constraint_node_for_places(negated, places);
}
fn record_narrowing_constraint_node_for_places(
&mut self,
constraint: ScopedNarrowingConstraint,
places: &PossiblyNarrowedPlaces,
) {
let pending = self.pending_reachability.current;
#[expect(
clippy::iter_over_hash_type,
reason = "the same constraint is recorded independently for each place"
)]
for place in places {
match place {
ScopedPlaceId::Symbol(symbol_id) => {
if let Some(state) = self.symbol_states.get_mut(*symbol_id) {
let state = self.pending_reachability.materialize(
state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
state.record_narrowing_constraint(
&mut self.narrowing_constraints,
constraint,
);
}
}
ScopedPlaceId::Member(member_id) => {
if let Some(state) = self.member_states.get_mut(*member_id) {
let state = self.pending_reachability.materialize(
state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
state.record_narrowing_constraint(
&mut self.narrowing_constraints,
constraint,
);
}
}
}
}
}
pub(super) fn single_symbol_snapshot(
&mut self,
symbol: ScopedSymbolId,
associated_member_ids: &[ScopedMemberId],
) -> SingleSymbolSnapshot {
let pending = self.pending_reachability.current;
let symbol_state = self
.pending_reachability
.materialize_ref(
&mut self.symbol_states[symbol],
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
)
.clone();
let mut associated_member_states = FxHashMap::default();
for &member_id in associated_member_ids {
let state = self.pending_reachability.materialize_ref(
&mut self.member_states[member_id],
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
associated_member_states.insert(member_id, state.clone());
}
SingleSymbolSnapshot {
symbol_state,
associated_member_states,
}
}
pub(super) fn record_and_negate_star_import_reachability_constraint(
&mut self,
reachability_id: ScopedReachabilityConstraintId,
symbol: ScopedSymbolId,
pre_definition: SingleSymbolSnapshot,
) {
self.checkpoint_state.record_binding_change();
let negated_reachability_id = self
.reachability_constraints
.add_not_constraint(reachability_id);
let pending = self.pending_reachability.current;
let symbol_state = self.pending_reachability.materialize(
&mut self.symbol_states[symbol],
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
let mut post_definition_state =
std::mem::replace(symbol_state, pre_definition.symbol_state);
post_definition_state
.record_reachability_constraint(&mut self.reachability_constraints, reachability_id);
symbol_state.record_reachability_constraint(
&mut self.reachability_constraints,
negated_reachability_id,
);
symbol_state.merge(
post_definition_state,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
#[expect(
clippy::iter_over_hash_type,
reason = "associated member states are merged independently"
)]
for (member_id, pre_definition_member_state) in pre_definition.associated_member_states {
let member_state = self.pending_reachability.materialize(
&mut self.member_states[member_id],
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
let mut post_definition_state =
std::mem::replace(member_state, pre_definition_member_state);
post_definition_state.record_reachability_constraint(
&mut self.reachability_constraints,
reachability_id,
);
member_state.record_reachability_constraint(
&mut self.reachability_constraints,
negated_reachability_id,
);
member_state.merge(
post_definition_state,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
}
}
pub(super) fn record_reachability_constraint(
&mut self,
reachability_constraint: ScopedReachabilityConstraintId,
) {
self.checkpoint_flow = self
.reachability_constraints
.add_and_constraint(self.checkpoint_flow, reachability_constraint);
let narrowing_constraint = if self.reachability_narrowing_enabled {
self.reachability_constraints
.narrowing_gate(reachability_constraint, &mut self.narrowing_constraints)
} else {
ScopedNarrowingConstraint::ALWAYS_TRUE
};
self.record_reachability_constraint_impl(reachability_constraint, narrowing_constraint);
}
pub(super) fn record_non_terminal_call_constraints(
&mut self,
reachability_constraint: ScopedReachabilityConstraintId,
narrowing_constraint: ScopedNarrowingConstraint,
) {
self.checkpoint_state.record_call_gate();
self.record_reachability_constraint_impl(reachability_constraint, narrowing_constraint);
}
fn record_reachability_constraint_impl(
&mut self,
reachability_constraint: ScopedReachabilityConstraintId,
narrowing_constraint: ScopedNarrowingConstraint,
) {
self.reachability = self
.reachability_constraints
.add_and_constraint(self.reachability, reachability_constraint);
self.pending_reachability
.push(reachability_constraint, narrowing_constraint);
}
pub(super) fn record_declaration(
&mut self,
place: ScopedPlaceId,
declaration: Definition<'db>,
) {
let def_id = self.push_definition(DefinitionEntry::Unused(declaration));
let pending = self.pending_reachability.current;
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let place_state = self.pending_reachability.materialize(
place_state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
self.definitions_by_definition.insert(
declaration,
DefinitionsAtDefinition {
bindings: place_state.bindings().clone(),
declarations: None,
},
);
place_state.record_declaration(def_id, self.reachability);
let definitions = match place {
ScopedPlaceId::Symbol(symbol) => &mut self.reachable_symbol_definitions[symbol],
ScopedPlaceId::Member(member) => &mut self.reachable_member_definitions[member],
};
definitions.declarations.record_declaration(
def_id,
self.reachability,
PreviousDefinitions::AreKept,
);
}
pub(super) fn record_combined_definition(
&mut self,
place: ScopedPlaceId,
definition: Definition<'db>,
part: DefinitionCategory,
) {
let entry = if part.is_binding() {
DefinitionEntry::Unused(definition)
} else {
DefinitionEntry::DeclarationPart(definition)
};
let def_id = self.push_definition(entry);
let pending = self.pending_reachability.current;
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let place_state = self.pending_reachability.materialize(
place_state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
let reachable_definitions = match place {
ScopedPlaceId::Symbol(symbol) => &mut self.reachable_symbol_definitions[symbol],
ScopedPlaceId::Member(member) => &mut self.reachable_member_definitions[member],
};
if part.is_declaration() {
place_state.record_declaration(def_id, self.reachability);
reachable_definitions.declarations.record_declaration(
def_id,
self.reachability,
PreviousDefinitions::AreKept,
);
}
if part.is_binding() {
place_state.record_binding(
def_id,
self.reachability,
self.is_class_scope,
place.is_symbol(),
PreviousDefinitions::AreShadowed,
FutureDefinitions::ShadowThisOne,
);
reachable_definitions.bindings.record_binding(
def_id,
self.reachability,
self.is_class_scope,
place.is_symbol(),
PreviousDefinitions::AreKept,
FutureDefinitions::ShadowThisOne,
);
}
}
pub(super) fn delete_binding(&mut self, place: ScopedPlaceId) {
let def_id = self.push_definition(DefinitionEntry::Deleted);
let pending = self.pending_reachability.current;
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let place_state = self.pending_reachability.materialize(
place_state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
place_state.record_binding(
def_id,
self.reachability,
self.is_class_scope,
place.is_symbol(),
PreviousDefinitions::AreShadowed,
FutureDefinitions::ShadowThisOne,
);
}
pub(super) fn record_use(&mut self, place: ScopedPlaceId, use_id: ScopedUseId) {
let pending = self.pending_reachability.current;
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let place_state = self.pending_reachability.materialize_ref_at_use(
place_state,
pending,
&mut self.reachability_constraints,
);
let bindings = place_state.bindings().clone();
self.record_use_bindings(bindings, use_id);
}
pub(super) fn record_multi_use(
&mut self,
places: impl Iterator<Item = ScopedPlaceId>,
use_id: ScopedUseId,
) {
let pending = self.pending_reachability.current;
for place in places {
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let place_state = self.pending_reachability.materialize_ref_at_use(
place_state,
pending,
&mut self.reachability_constraints,
);
let bindings = place_state.bindings().clone();
let binding_definition_ids = bindings.iter().map(LiveBinding::binding);
self.mark_definition_ids_used(binding_definition_ids);
self.multi_bindings_by_use
.entry(use_id)
.or_default()
.push(bindings);
}
self.record_use_bindings(Bindings::default(), use_id);
}
fn record_use_bindings(&mut self, bindings: Bindings, use_id: ScopedUseId) {
let binding_definition_ids = bindings.iter().map(LiveBinding::binding);
self.mark_definition_ids_used(binding_definition_ids);
let new_use = self.bindings_by_use.push(bindings);
debug_assert_eq!(use_id, new_use);
}
pub(super) fn symbol_binding_definition_ids(
&self,
symbol: ScopedSymbolId,
) -> impl Iterator<Item = ScopedDefinitionId> + '_ {
self.symbol_states[symbol]
.state
.bindings()
.iter()
.map(LiveBinding::binding)
}
pub(super) fn symbol_live_binding_status(
&mut self,
symbol: ScopedSymbolId,
) -> LiveBindingStatus {
let mut has_binding = false;
let mut has_unbound = false;
for binding in self.current_bindings(symbol.into()) {
if binding.reachability_constraint() == ScopedReachabilityConstraintId::ALWAYS_FALSE {
continue;
}
if binding.binding().is_unbound() {
has_unbound = true;
} else {
has_binding = true;
}
}
match (has_binding, has_unbound) {
(true, true) => LiveBindingStatus::PossiblyBound,
(true, false) => LiveBindingStatus::Bound,
(false, _) => LiveBindingStatus::Unbound,
}
}
pub(super) fn mark_binding_definitions_used(
&mut self,
binding_definition_ids: impl IntoIterator<Item = ScopedDefinitionId>,
) {
self.mark_definition_ids_used(binding_definition_ids);
}
pub(super) fn record_range_reachability(
&mut self,
range: TextRange,
is_type_checking_block: bool,
) {
let this_range_info = RangeInfo {
reachability: self.reachability,
in_type_checking_block: is_type_checking_block,
};
if let Some((last_range, last_range_info)) = self.range_reachability.last_mut()
&& *last_range_info == this_range_info
{
*last_range = last_range.cover(range);
return;
}
self.range_reachability.push((range, this_range_info));
}
pub(super) fn snapshot_enclosing_state(
&mut self,
enclosing_place: ScopedPlaceId,
enclosing_scope: ScopeKind,
enclosing_place_expr: PlaceExprRef,
is_parent_of_annotation_scope: bool,
) -> ScopedEnclosingSnapshotId {
let pending = self.pending_reachability.current;
let place_state = pending_place_state_mut(
enclosing_place,
&mut self.symbol_states,
&mut self.member_states,
);
let bindings = self
.pending_reachability
.materialize_ref(
place_state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
)
.bindings();
let is_class_symbol = enclosing_scope.is_class() && enclosing_place.is_symbol();
let is_forwarding_symbol = enclosing_place_expr
.as_symbol()
.is_some_and(|symbol| symbol.is_global() || symbol.is_nonlocal());
let stores_visible_bindings = enclosing_place_expr.is_bound()
&& bindings
.iter()
.any(|binding| !binding.binding().is_unbound());
if (is_class_symbol && !is_parent_of_annotation_scope)
|| !enclosing_place_expr.is_bound()
|| (is_forwarding_symbol && !stores_visible_bindings)
{
self.enclosing_snapshots.push(EnclosingSnapshot::Constraint(
bindings.unbound_narrowing_constraint(),
))
} else {
self.enclosing_snapshots
.push(EnclosingSnapshot::Bindings(bindings.clone()))
}
}
pub(super) fn update_enclosing_snapshot(
&mut self,
snapshot_id: ScopedEnclosingSnapshotId,
enclosing_symbol: ScopedSymbolId,
) {
let pending = self.pending_reachability.current;
let new_bindings = self
.pending_reachability
.materialize_ref(
&mut self.symbol_states[enclosing_symbol],
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
)
.bindings()
.clone();
match self.enclosing_snapshots.get_mut(snapshot_id) {
Some(EnclosingSnapshot::Bindings(bindings)) => {
bindings.merge(
new_bindings,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
}
Some(EnclosingSnapshot::Constraint(constraint)) => {
*constraint = ScopedNarrowingConstraint::ALWAYS_TRUE;
}
None => {}
}
}
fn mark_definition_ids_used(
&mut self,
definition_ids: impl IntoIterator<Item = ScopedDefinitionId>,
) {
for definition_id in definition_ids {
self.mark_definition_used(definition_id);
}
}
fn mark_definition_used(&mut self, definition_id: ScopedDefinitionId) {
let entry = &mut self.all_definitions[definition_id];
if let DefinitionEntry::Unused(definition) = *entry {
*entry = DefinitionEntry::Used(definition);
}
}
pub(super) fn snapshot(&self) -> FlowSnapshot {
FlowSnapshot {
symbol_states: self.symbol_states.clone(),
member_states: self.member_states.clone(),
reachability: self.reachability,
checkpoint_flow: self.checkpoint_flow,
checkpoint_state: self.checkpoint_state.snapshot(),
pending_reachability: self.pending_reachability.current,
}
}
pub(super) fn current_bindings(
&mut self,
place: ScopedPlaceId,
) -> impl Iterator<Item = LiveBinding> + '_ {
let pending = self.pending_reachability.current;
let place_state =
pending_place_state_mut(place, &mut self.symbol_states, &mut self.member_states);
let bindings = self
.pending_reachability
.materialize_ref(
place_state,
pending,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
)
.bindings();
bindings.iter().copied()
}
pub(super) fn restore(&mut self, snapshot: FlowSnapshot) {
self.checkpoint_state.restore(snapshot.checkpoint_state);
let num_symbols = self.symbol_states.len();
let num_members = self.member_states.len();
debug_assert!(num_symbols >= snapshot.symbol_states.len());
self.symbol_states = snapshot.symbol_states;
self.member_states = snapshot.member_states;
self.reachability = snapshot.reachability;
self.checkpoint_flow = snapshot.checkpoint_flow;
self.pending_reachability.current = snapshot.pending_reachability;
let undefined = PendingPlaceState::new(
PlaceState::undefined(self.reachability),
self.pending_reachability.current,
);
self.symbol_states.resize(num_symbols, undefined.clone());
self.member_states.resize(num_members, undefined);
}
pub(super) fn merge(&mut self, snapshot: FlowSnapshot) {
if snapshot.reachability == ScopedReachabilityConstraintId::ALWAYS_FALSE {
return;
}
if self.reachability == ScopedReachabilityConstraintId::ALWAYS_FALSE {
self.restore(snapshot);
return;
}
self.checkpoint_state.merge(snapshot.checkpoint_state);
debug_assert!(self.symbol_states.len() >= snapshot.symbol_states.len());
debug_assert!(self.member_states.len() >= snapshot.member_states.len());
let branch = snapshot.pending_reachability;
self.pending_reachability.merge_place_states(
&mut self.symbol_states,
snapshot.symbol_states,
branch,
snapshot.reachability,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
self.pending_reachability.merge_place_states(
&mut self.member_states,
snapshot.member_states,
branch,
snapshot.reachability,
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
self.reachability = self
.reachability_constraints
.add_or_constraint(self.reachability, snapshot.reachability);
self.checkpoint_flow = self
.reachability_constraints
.add_or_constraint(self.checkpoint_flow, snapshot.checkpoint_flow);
}
pub(super) fn finish(mut self: Box<Self>) -> UseDefMap<'db> {
let pending = self.pending_reachability.current;
for state in self
.symbol_states
.iter_mut()
.chain(self.member_states.iter_mut())
{
self.pending_reachability.materialize_reachability(
state,
pending,
&mut self.reachability_constraints,
);
}
let place_state_count = self.symbol_states.len()
+ self.member_states.len()
+ self.reachable_symbol_definitions.len()
+ self.reachable_member_definitions.len();
let definitions_with_declarations_count = self
.definitions_by_definition
.values()
.filter(|definitions| definitions.declarations.is_some())
.count();
let interned_bindings_capacity = self.definitions_by_definition.len()
+ self.bindings_by_use.len()
+ self.enclosing_snapshots.len()
+ place_state_count;
let interned_declarations_capacity =
definitions_with_declarations_count + place_state_count;
let interned_ids_by_declarations_capacity =
definitions_with_declarations_count + self.member_states.len();
let mut place_state_interner = PlaceStateInterner::with_capacity(
interned_bindings_capacity,
interned_ids_by_declarations_capacity,
interned_declarations_capacity,
);
let definitions_by_definition = Self::intern_definitions_by_definition(
self.definitions_by_definition,
&mut place_state_interner,
);
let bindings_by_use =
Self::intern_bindings_by_use(self.bindings_by_use, &mut place_state_interner);
let symbol_states = self
.symbol_states
.into_iter()
.map(|state| Rc::unwrap_or_clone(state.state))
.collect();
let member_states = self
.member_states
.into_iter()
.map(|state| Rc::unwrap_or_clone(state.state))
.collect();
let end_of_scope_symbols = Self::intern_place_states(
symbol_states,
PlaceState::into_parts,
&mut place_state_interner,
);
let end_of_scope_members =
Self::intern_end_of_scope_members(member_states, &mut place_state_interner);
let reachable_definitions_by_symbol = Self::intern_place_states(
self.reachable_symbol_definitions,
|definitions| (definitions.bindings, definitions.declarations),
&mut place_state_interner,
);
let reachable_definitions_by_member = Self::intern_place_states(
self.reachable_member_definitions,
|definitions| (definitions.bindings, definitions.declarations),
&mut place_state_interner,
);
let enclosing_snapshots =
Self::intern_enclosing_snapshots(self.enclosing_snapshots, &mut place_state_interner);
let PlaceStateInterner {
interned_bindings,
interned_declarations,
..
} = place_state_interner;
let interned_bindings = interned_bindings.finish(
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
let interned_declarations =
interned_declarations.finish(&mut self.reachability_constraints);
for bindings in self.multi_bindings_by_use.values_mut().flatten() {
bindings.finish(
&mut self.narrowing_constraints,
&mut self.reachability_constraints,
);
}
self.range_reachability
.retain(|(_, info)| *info != RangeInfo::default());
for &(_, RangeInfo { reachability, .. }) in &self.range_reachability {
self.reachability_constraints.mark_used(reachability);
}
for enclosing_snapshot in &enclosing_snapshots {
if let InternedEnclosingSnapshotId::Constraint(constraint) = enclosing_snapshot {
self.narrowing_constraints.mark_used(*constraint);
}
}
self.reachability_constraints.mark_used(self.reachability);
let symbol_states =
Self::zip_place_states(end_of_scope_symbols, reachable_definitions_by_symbol);
let member_states =
Self::zip_place_states(end_of_scope_members, reachable_definitions_by_member);
let multi_bindings_by_use = MultiBindingsByUse::from_map(self.multi_bindings_by_use);
let loop_headers = self.loop_headers;
let extra = (!bindings_by_use.is_empty()
|| !member_states.is_empty()
|| !enclosing_snapshots.is_empty()
|| !loop_headers.is_empty())
.then(|| {
Box::new(UseDefMapExtra {
bindings_by_use: bindings_by_use.into(),
multi_bindings_by_use,
member_states,
enclosing_snapshots: enclosing_snapshots.into(),
loop_headers: loop_headers.into(),
})
});
let predicates = self.predicates.build();
let predicate_narrowing_targets =
PredicateNarrowingTargets::from_entries(self.predicate_narrowing_targets);
let reachability_constraints = self.reachability_constraints.build();
let narrowing_constraints = self.narrowing_constraints.build();
let constraint_tables = (!reachability_constraints.used_interiors().is_empty()
|| !narrowing_constraints.is_empty())
.then(|| {
Box::new(ConstraintTables {
predicates,
predicate_narrowing_targets,
reachability_constraints,
narrowing_constraints,
})
});
let all_definitions = RetainedDefinitions::new(self.all_definitions);
UseDefMap {
all_definitions,
constraint_tables,
interned_bindings,
interned_declarations,
range_reachability: self.range_reachability.into_boxed_slice(),
symbol_states,
definitions_by_definition,
extra,
end_of_scope_reachability: self.reachability,
}
}
fn zip_place_states<I: Idx, T>(
end_of_scope: IndexVec<I, T>,
reachable: IndexVec<I, T>,
) -> FrozenIndexVec<I, RetainedPlaceStates<T>> {
assert_eq!(end_of_scope.len(), reachable.len());
end_of_scope
.into_iter()
.zip(reachable)
.map(|(end_of_scope, reachable)| RetainedPlaceStates {
end_of_scope,
reachable,
})
.collect()
}
fn intern_definitions_by_definition(
definitions_by_definition: FxHashMap<
Definition<'db>,
DefinitionsAtDefinition<Bindings, Declarations>,
>,
place_state_interner: &mut PlaceStateInterner,
) -> FrozenMap<
Definition<'db>,
DefinitionsAtDefinition<InternedBindingsId, InternedDeclarationsId>,
> {
let mut interned_ids_by_definition = Vec::with_capacity(definitions_by_definition.len());
let mut definitions_by_definition =
definitions_by_definition.into_iter().collect::<Vec<_>>();
definitions_by_definition.sort_unstable_by_key(|(definition, _)| *definition);
for (
definition,
DefinitionsAtDefinition {
bindings,
declarations,
},
) in definitions_by_definition
{
if bindings.is_always_unbound()
&& declarations
.as_ref()
.is_none_or(Declarations::is_always_undeclared)
{
continue;
}
let bindings = place_state_interner.intern_bindings(&bindings);
let declarations = declarations
.map(|declarations| place_state_interner.intern_declarations(declarations));
interned_ids_by_definition.push((
definition,
DefinitionsAtDefinition {
bindings,
declarations,
},
));
}
FrozenMap::from_entries(interned_ids_by_definition)
}
fn intern_bindings_by_use(
bindings_by_use: IndexVec<ScopedUseId, Bindings>,
place_state_interner: &mut PlaceStateInterner,
) -> IndexVec<ScopedUseId, InternedBindingsId> {
let mut interned_ids_by_use: IndexVec<ScopedUseId, InternedBindingsId> =
IndexVec::with_capacity(bindings_by_use.len());
for bindings in bindings_by_use {
let interned_id = place_state_interner.intern_bindings(&bindings);
interned_ids_by_use.push(interned_id);
}
interned_ids_by_use
}
fn intern_place_states<I: Idx, T>(
place_states: IndexVec<I, T>,
get_parts: impl Fn(T) -> (Bindings, Declarations),
place_state_interner: &mut PlaceStateInterner,
) -> IndexVec<I, InternedPlaceStateId> {
let mut interned_ids_by_place = IndexVec::with_capacity(place_states.len());
for place_state in place_states {
let (bindings, declarations) = get_parts(place_state);
let interned_id = place_state_interner.retain_place_state(&bindings, declarations);
interned_ids_by_place.push(interned_id);
}
interned_ids_by_place
}
fn intern_end_of_scope_members(
end_of_scope_members: IndexVec<ScopedMemberId, PlaceState>,
place_state_interner: &mut PlaceStateInterner,
) -> IndexVec<ScopedMemberId, InternedPlaceStateId> {
let mut interned_ids_by_member = IndexVec::with_capacity(end_of_scope_members.len());
let mut interned_ids_by_place_state =
FxHashMap::with_capacity_and_hasher(end_of_scope_members.len(), FxBuildHasher);
for place_state in end_of_scope_members {
let interned_id = match interned_ids_by_place_state.entry(place_state) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let place_state = entry.key();
let interned_id = place_state_interner.intern_place_state(
place_state.bindings(),
place_state.declarations().clone(),
);
entry.insert(interned_id);
interned_id
}
};
interned_ids_by_member.push(interned_id);
}
interned_ids_by_member
}
fn intern_enclosing_snapshots(
enclosing_snapshots: EnclosingSnapshots,
place_state_interner: &mut PlaceStateInterner,
) -> IndexVec<ScopedEnclosingSnapshotId, InternedEnclosingSnapshotId> {
let mut interned_ids_by_snapshot: IndexVec<
ScopedEnclosingSnapshotId,
InternedEnclosingSnapshotId,
> = IndexVec::with_capacity(enclosing_snapshots.len());
for snapshot in enclosing_snapshots {
let interned_id = match snapshot {
EnclosingSnapshot::Bindings(bindings) => {
let interned_bindings_id = place_state_interner.intern_bindings(&bindings);
InternedEnclosingSnapshotId::Bindings(interned_bindings_id)
}
EnclosingSnapshot::Constraint(constraint) => {
InternedEnclosingSnapshotId::Constraint(constraint)
}
};
interned_ids_by_snapshot.push(interned_id);
}
interned_ids_by_snapshot
}
}