diff --git a/crates/red_knot_python_semantic/src/semantic_index.rs b/crates/red_knot_python_semantic/src/semantic_index.rs index 52d7b1298f..8ebdc1db59 100644 --- a/crates/red_knot_python_semantic/src/semantic_index.rs +++ b/crates/red_knot_python_semantic/src/semantic_index.rs @@ -27,7 +27,7 @@ pub mod definition; pub mod expression; pub mod symbol; mod use_def; -pub(crate) mod visibility_constraint; +pub(crate) mod visibility_constraints; pub(crate) use self::use_def::{ BindingWithConstraints, BindingWithConstraintsIterator, DeclarationsIterator, diff --git a/crates/red_knot_python_semantic/src/semantic_index/builder.rs b/crates/red_knot_python_semantic/src/semantic_index/builder.rs index bd62633d8a..2f7911f6cf 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/builder.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/builder.rs @@ -28,7 +28,7 @@ use crate::semantic_index::symbol::{ use crate::semantic_index::use_def::{ FlowSnapshot, ScopedConstraintId, ScopedVisibilityConstraintId, UseDefMapBuilder, }; -use crate::semantic_index::visibility_constraint::VisibilityConstraintRef; +use crate::semantic_index::visibility_constraints::VisibilityConstraintRef; use crate::semantic_index::SemanticIndex; use crate::unpack::Unpack; use crate::Db; diff --git a/crates/red_knot_python_semantic/src/semantic_index/use_def.rs b/crates/red_knot_python_semantic/src/semantic_index/use_def.rs index 047f75d5b2..5068d2dfe8 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/use_def.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/use_def.rs @@ -229,7 +229,9 @@ pub(crate) use self::symbol_state::{ScopedConstraintId, ScopedVisibilityConstrai use crate::semantic_index::ast_ids::ScopedUseId; use crate::semantic_index::definition::Definition; use crate::semantic_index::symbol::ScopedSymbolId; -pub(crate) use crate::semantic_index::visibility_constraint::VisibilityConstraintRef; +use crate::semantic_index::visibility_constraints::{ + VisibilityConstraintRef, VisibilityConstraints, +}; use ruff_index::IndexVec; use rustc_hash::FxHashMap; @@ -248,7 +250,7 @@ pub(crate) struct UseDefMap<'db> { all_constraints: IndexVec>, /// Array of [`VisibilityConstraintRef`] in this scope. - all_visibility_constraints: IndexVec, + all_visibility_constraints: VisibilityConstraints, /// [`SymbolBindings`] reaching a [`ScopedUseId`]. bindings_by_use: IndexVec, @@ -389,8 +391,7 @@ pub(crate) struct BindingWithConstraints<'map, 'db> { pub(crate) binding: Option>, pub(crate) constraints: ConstraintsIterator<'map, 'db>, pub(crate) all_constraints: &'map IndexVec>, - pub(crate) all_visibility_constraints: - &'map IndexVec, + pub(crate) all_visibility_constraints: &'map VisibilityConstraints, pub(crate) visibility_constraint: ScopedVisibilityConstraintId, } @@ -420,7 +421,7 @@ impl<'map, 'db> Iterator for DeclarationsIterator<'map, 'db> { type Item = ( Option>, &'map IndexVec>, - &'map IndexVec, + &'map VisibilityConstraints, ScopedVisibilityConstraintId, ); @@ -456,7 +457,7 @@ pub(super) struct UseDefMapBuilder<'db> { all_constraints: IndexVec>, /// Append-only array of [`VisibilityConstraintRef`]. - all_visibility_constraints: IndexVec, + all_visibility_constraints: VisibilityConstraints, unbound_visibility_constraint_id: ScopedVisibilityConstraintId, @@ -475,7 +476,7 @@ impl<'db> UseDefMapBuilder<'db> { Self { all_definitions: IndexVec::from_iter([None]), all_constraints: IndexVec::new(), - all_visibility_constraints: IndexVec::from_iter([VisibilityConstraintRef::None]), + all_visibility_constraints: VisibilityConstraints::new(), unbound_visibility_constraint_id: ScopedVisibilityConstraintId::from_u32(0), bindings_by_use: IndexVec::new(), definitions_by_definition: FxHashMap::default(), @@ -512,7 +513,7 @@ impl<'db> UseDefMapBuilder<'db> { &mut self, constraint: VisibilityConstraintRef, ) -> ScopedVisibilityConstraintId { - self.all_visibility_constraints.push(constraint) + self.all_visibility_constraints.add(constraint) } pub(super) fn record_visibility_constraint( @@ -532,7 +533,7 @@ impl<'db> UseDefMapBuilder<'db> { } else { self.unbound_visibility_constraint_id = self.all_visibility_constraints - .push(VisibilityConstraintRef::Sequence( + .add(VisibilityConstraintRef::Sequence( self.unbound_visibility_constraint_id, new_constraint_id, )); @@ -644,31 +645,11 @@ impl<'db> UseDefMapBuilder<'db> { } // Merge unbound visibility constraints: - match ( - &self.all_visibility_constraints[self.unbound_visibility_constraint_id], - &self.all_visibility_constraints[snapshot.unbound_visibility_constraint_id], - ) { - (_, VisibilityConstraintRef::Negated(id)) - if self.unbound_visibility_constraint_id == *id => - { - self.unbound_visibility_constraint_id = ScopedVisibilityConstraintId::from_u32(0); - } - (VisibilityConstraintRef::Negated(id), _) - if *id == snapshot.unbound_visibility_constraint_id => - { - self.unbound_visibility_constraint_id = ScopedVisibilityConstraintId::from_u32(0); - } - _ => { - let constraint_id = - self.all_visibility_constraints - .push(VisibilityConstraintRef::Merged( - self.unbound_visibility_constraint_id, - snapshot.unbound_visibility_constraint_id, - )); - self.unbound_visibility_constraint_id = constraint_id; - } - } + self.unbound_visibility_constraint_id = self.all_visibility_constraints.add_merged( + self.unbound_visibility_constraint_id, + snapshot.unbound_visibility_constraint_id, + ); } pub(super) fn finish(mut self) -> UseDefMap<'db> { diff --git a/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs b/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs index 4f9f9ccb38..7be9fe0c9d 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs @@ -43,7 +43,7 @@ //! //! Tracking live declarations is simpler, since constraints are not involved, but otherwise very //! similar to tracking live bindings. -use crate::semantic_index::use_def::{Constraint, VisibilityConstraintRef}; +use crate::semantic_index::use_def::{Constraint, VisibilityConstraintRef, VisibilityConstraints}; use super::bitset::{BitSet, BitSetIterator}; use ruff_index::{newtype_index, IndexVec}; @@ -131,10 +131,7 @@ impl SymbolDeclarations { /// Add given visibility constraint to all live bindings. pub(super) fn record_visibility_constraint( &mut self, - all_visibility_constraints: &mut IndexVec< - ScopedVisibilityConstraintId, - VisibilityConstraintRef, - >, + all_visibility_constraints: &mut VisibilityConstraints, constraint: ScopedVisibilityConstraintId, ) { for existing in &mut self.visibility_constraints { @@ -142,7 +139,7 @@ impl SymbolDeclarations { *existing = constraint; } else { *existing = all_visibility_constraints - .push(VisibilityConstraintRef::Sequence(*existing, constraint)); + .add(VisibilityConstraintRef::Sequence(*existing, constraint)); } } } @@ -214,10 +211,7 @@ impl SymbolBindings { /// Add given visibility constraint to all live bindings. pub(super) fn record_visibility_constraint( &mut self, - all_visibility_constraints: &mut IndexVec< - ScopedVisibilityConstraintId, - VisibilityConstraintRef, - >, + all_visibility_constraints: &mut VisibilityConstraints, constraint: ScopedVisibilityConstraintId, ) { for existing in &mut self.visibility_constraints { @@ -225,7 +219,7 @@ impl SymbolBindings { *existing = constraint; } else { *existing = all_visibility_constraints - .push(VisibilityConstraintRef::Sequence(*existing, constraint)); + .add(VisibilityConstraintRef::Sequence(*existing, constraint)); } } } @@ -234,10 +228,7 @@ impl SymbolBindings { pub(super) fn iter<'map, 'db>( &'map self, all_constraints: &'map IndexVec>, - all_visibility_constraints: &'map IndexVec< - ScopedVisibilityConstraintId, - VisibilityConstraintRef, - >, + all_visibility_constraints: &'map VisibilityConstraints, ) -> BindingIdWithConstraintsIterator<'map, 'db> { BindingIdWithConstraintsIterator { all_constraints, @@ -277,10 +268,7 @@ impl SymbolState { /// Add given visibility constraint to all live bindings. pub(super) fn record_visibility_constraint( &mut self, - all_visibility_constraints: &mut IndexVec< - ScopedVisibilityConstraintId, - VisibilityConstraintRef, - >, + all_visibility_constraints: &mut VisibilityConstraints, constraint: ScopedVisibilityConstraintId, ) { self.bindings @@ -308,10 +296,7 @@ impl SymbolState { pub(super) fn merge( &mut self, b: SymbolState, - all_visibility_constraints: &mut IndexVec< - ScopedVisibilityConstraintId, - VisibilityConstraintRef, - >, + all_visibility_constraints: &mut VisibilityConstraints, ) { let mut a = Self { bindings: SymbolBindings { @@ -422,26 +407,8 @@ impl SymbolState { .next() .expect("visibility_constraints length mismatch"); let current = self.bindings.visibility_constraints.last_mut().unwrap(); - match ( - &all_visibility_constraints[*current], - &all_visibility_constraints[a_vis_constraint], - ) { - (_, VisibilityConstraintRef::Negated(id)) if current == id => { - *current = ScopedVisibilityConstraintId::from_u32(0); - } - - (VisibilityConstraintRef::Negated(id), _) - if *id == a_vis_constraint => - { - *current = ScopedVisibilityConstraintId::from_u32(0); - } - _ => { - let constraint_id = all_visibility_constraints.push( - VisibilityConstraintRef::Merged(*current, a_vis_constraint), - ); - *current = constraint_id; - } - } + *current = + all_visibility_constraints.add_merged(*current, a_vis_constraint); opt_a_def = a_defs_iter.next(); opt_b_def = b_defs_iter.next(); @@ -507,26 +474,8 @@ impl SymbolState { .next() .expect("declarations and visibility_constraints length mismatch"); let current = self.declarations.visibility_constraints.last_mut().unwrap(); - match ( - &all_visibility_constraints[*current], - &all_visibility_constraints[a_vis_constraint], - ) { - (_, VisibilityConstraintRef::Negated(id)) if current == id => { - *current = ScopedVisibilityConstraintId::from_u32(0); - } - - (VisibilityConstraintRef::Negated(id), _) - if *id == a_vis_constraint => - { - *current = ScopedVisibilityConstraintId::from_u32(0); - } - _ => { - let constraint_id = all_visibility_constraints.push( - VisibilityConstraintRef::Merged(*current, a_vis_constraint), - ); - *current = constraint_id; - } - } + *current = + all_visibility_constraints.add_merged(*current, a_vis_constraint); opt_a_decl = a_decls_iter.next(); opt_b_decl = b_decls_iter.next(); @@ -561,16 +510,14 @@ pub(super) struct BindingIdWithConstraints<'map, 'db> { pub(super) definition: ScopedDefinitionId, pub(super) constraint_ids: ConstraintIdIterator<'map>, pub(super) all_constraints: &'map IndexVec>, - pub(super) all_visibility_constraints: - &'map IndexVec, + pub(super) all_visibility_constraints: &'map VisibilityConstraints, pub(super) visibility_constraint: ScopedVisibilityConstraintId, } #[derive(Debug)] pub(super) struct BindingIdWithConstraintsIterator<'map, 'db> { all_constraints: &'map IndexVec>, - all_visibility_constraints: - &'map IndexVec, + all_visibility_constraints: &'map VisibilityConstraints, definitions: BindingsIterator<'map>, constraints: ConstraintsIterator<'map>, visibility_constraints: VisibilityConstraintsIterator<'map>, @@ -622,8 +569,7 @@ impl std::iter::FusedIterator for ConstraintIdIterator<'_> {} pub(super) struct DeclarationIdIterator<'map, 'db> { pub(crate) all_constraints: &'map IndexVec>, - pub(crate) all_visibility_constraints: - &'map IndexVec, + pub(crate) all_visibility_constraints: &'map VisibilityConstraints, pub(crate) inner: DeclarationsIterator<'map>, pub(crate) visibility_constraints: VisibilityConstraintsIterator<'map>, } @@ -632,7 +578,7 @@ impl<'map, 'db> Iterator for DeclarationIdIterator<'map, 'db> { type Item = ( ScopedDefinitionId, &'map IndexVec>, - &'map IndexVec, + &'map VisibilityConstraints, ScopedVisibilityConstraintId, ); diff --git a/crates/red_knot_python_semantic/src/semantic_index/visibility_constraint.rs b/crates/red_knot_python_semantic/src/semantic_index/visibility_constraint.rs deleted file mode 100644 index 761db19733..0000000000 --- a/crates/red_knot_python_semantic/src/semantic_index/visibility_constraint.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::semantic_index::use_def::{ScopedConstraintId, ScopedVisibilityConstraintId}; - -/// TODO -/// -/// Used to represent active branching conditions that apply to a particular definition. -/// A definition can either be conditional on a specific constraint from a `if`, `elif`, -/// `while` statement, an `if`-expression, or a Boolean expression. Or it can be marked -/// as 'ambiguous' if it occurred in a control-flow path that is not conditional on any -/// specific expression that can be statically analyzed (`for` loop, `try` ... `except`). -/// -/// -/// For example: -/// ```py -/// a = 1 # no visibility constraints -/// -/// if test1: -/// b = 1 # Constraint(test1) -/// -/// if test2: -/// c = 1 # Constraint(test1), Constraint(test2) -/// -/// for _ in range(10): -/// d = 1 # Constraint(test1), Ambiguous -/// else: -/// d = 1 # Constraint(~test1) -/// ``` -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum VisibilityConstraintRef { - None, - Single(ScopedConstraintId), - Negated(ScopedVisibilityConstraintId), - Sequence(ScopedVisibilityConstraintId, ScopedVisibilityConstraintId), - Merged(ScopedVisibilityConstraintId, ScopedVisibilityConstraintId), -} diff --git a/crates/red_knot_python_semantic/src/semantic_index/visibility_constraints.rs b/crates/red_knot_python_semantic/src/semantic_index/visibility_constraints.rs new file mode 100644 index 0000000000..ac0bb40d34 --- /dev/null +++ b/crates/red_knot_python_semantic/src/semantic_index/visibility_constraints.rs @@ -0,0 +1,82 @@ +use std::ops::Index; + +use ruff_index::IndexVec; + +use crate::semantic_index::{ScopedConstraintId, ScopedVisibilityConstraintId}; + +/// TODO +/// +/// Used to represent active branching conditions that apply to a particular definition. +/// A definition can either be conditional on a specific constraint from a `if`, `elif`, +/// `while` statement, an `if`-expression, or a Boolean expression. Or it can be marked +/// as 'ambiguous' if it occurred in a control-flow path that is not conditional on any +/// specific expression that can be statically analyzed (`for` loop, `try` ... `except`). +/// +/// +/// For example: +/// ```py +/// a = 1 # no visibility constraints +/// +/// if test1: +/// b = 1 # Constraint(test1) +/// +/// if test2: +/// c = 1 # Constraint(test1), Constraint(test2) +/// +/// for _ in range(10): +/// d = 1 # Constraint(test1), Ambiguous +/// else: +/// d = 1 # Constraint(~test1) +/// ``` +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum VisibilityConstraintRef { + None, + Single(ScopedConstraintId), + Negated(ScopedVisibilityConstraintId), + Sequence(ScopedVisibilityConstraintId, ScopedVisibilityConstraintId), + Merged(ScopedVisibilityConstraintId, ScopedVisibilityConstraintId), +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct VisibilityConstraints { + constraints: IndexVec, +} + +impl VisibilityConstraints { + pub(crate) fn new() -> Self { + Self { + constraints: IndexVec::from_iter([VisibilityConstraintRef::None]), + } + } + + pub(crate) fn add( + &mut self, + constraint: VisibilityConstraintRef, + ) -> ScopedVisibilityConstraintId { + self.constraints.push(constraint) + } + + pub(crate) fn add_merged( + &mut self, + a: ScopedVisibilityConstraintId, + b: ScopedVisibilityConstraintId, + ) -> ScopedVisibilityConstraintId { + match (&self.constraints[a], &self.constraints[b]) { + (_, VisibilityConstraintRef::Negated(id)) if a == *id => { + ScopedVisibilityConstraintId::from_u32(0) + } + (VisibilityConstraintRef::Negated(id), _) if *id == b => { + ScopedVisibilityConstraintId::from_u32(0) + } + _ => self.add(VisibilityConstraintRef::Merged(a, b)), + } + } +} + +impl Index for VisibilityConstraints { + type Output = VisibilityConstraintRef; + + fn index(&self, index: ScopedVisibilityConstraintId) -> &Self::Output { + &self.constraints[index] + } +} diff --git a/crates/red_knot_python_semantic/src/types/static_visibility.rs b/crates/red_knot_python_semantic/src/types/static_visibility.rs index 456a574fd0..acba254742 100644 --- a/crates/red_knot_python_semantic/src/types/static_visibility.rs +++ b/crates/red_knot_python_semantic/src/types/static_visibility.rs @@ -3,7 +3,8 @@ use ruff_index::IndexVec; use crate::semantic_index::{ ast_ids::HasScopedExpressionId, constraint::{Constraint, ConstraintNode, PatternConstraintKind}, - visibility_constraint::VisibilityConstraintRef, + visibility_constraints::VisibilityConstraintRef, + visibility_constraints::VisibilityConstraints, ScopedConstraintId, ScopedVisibilityConstraintId, }; use crate::types::{infer_expression_types, Truthiness}; @@ -15,7 +16,7 @@ const MAX_RECURSION_DEPTH: usize = 10; pub(crate) fn analyze<'db>( db: &'db dyn Db, all_constraints: &IndexVec>, - all_visibility_constraints: &IndexVec, + all_visibility_constraints: &VisibilityConstraints, visibility_constraint_id: ScopedVisibilityConstraintId, ) -> Truthiness { analyze_impl( @@ -30,7 +31,7 @@ pub(crate) fn analyze<'db>( fn analyze_impl<'db>( db: &'db dyn Db, all_constraints: &IndexVec>, - all_visibility_constraints: &IndexVec, + all_visibility_constraints: &VisibilityConstraints, visibility_constraint_id: ScopedVisibilityConstraintId, max_depth: usize, ) -> Truthiness {