Introduce VisibilityConstraints struct

This commit is contained in:
David Peter
2024-12-18 08:50:44 +01:00
parent 3a1dbc182f
commit 4f6fba2cab
7 changed files with 118 additions and 142 deletions

View File

@@ -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,

View File

@@ -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;

View File

@@ -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<ScopedConstraintId, Constraint<'db>>,
/// Array of [`VisibilityConstraintRef`] in this scope.
all_visibility_constraints: IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
all_visibility_constraints: VisibilityConstraints,
/// [`SymbolBindings`] reaching a [`ScopedUseId`].
bindings_by_use: IndexVec<ScopedUseId, SymbolBindings>,
@@ -389,8 +391,7 @@ pub(crate) struct BindingWithConstraints<'map, 'db> {
pub(crate) binding: Option<Definition<'db>>,
pub(crate) constraints: ConstraintsIterator<'map, 'db>,
pub(crate) all_constraints: &'map IndexVec<ScopedConstraintId, Constraint<'db>>,
pub(crate) all_visibility_constraints:
&'map IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
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<Definition<'db>>,
&'map IndexVec<ScopedConstraintId, Constraint<'db>>,
&'map IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
&'map VisibilityConstraints,
ScopedVisibilityConstraintId,
);
@@ -456,7 +457,7 @@ pub(super) struct UseDefMapBuilder<'db> {
all_constraints: IndexVec<ScopedConstraintId, Constraint<'db>>,
/// Append-only array of [`VisibilityConstraintRef`].
all_visibility_constraints: IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
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> {

View File

@@ -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<ScopedConstraintId, Constraint<'db>>,
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<ScopedConstraintId, Constraint<'db>>,
pub(super) all_visibility_constraints:
&'map IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
pub(super) all_visibility_constraints: &'map VisibilityConstraints,
pub(super) visibility_constraint: ScopedVisibilityConstraintId,
}
#[derive(Debug)]
pub(super) struct BindingIdWithConstraintsIterator<'map, 'db> {
all_constraints: &'map IndexVec<ScopedConstraintId, Constraint<'db>>,
all_visibility_constraints:
&'map IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
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<ScopedConstraintId, Constraint<'db>>,
pub(crate) all_visibility_constraints:
&'map IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
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<ScopedConstraintId, Constraint<'db>>,
&'map IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
&'map VisibilityConstraints,
ScopedVisibilityConstraintId,
);

View File

@@ -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),
}

View File

@@ -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<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
}
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<ScopedVisibilityConstraintId> for VisibilityConstraints {
type Output = VisibilityConstraintRef;
fn index(&self, index: ScopedVisibilityConstraintId) -> &Self::Output {
&self.constraints[index]
}
}

View File

@@ -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<ScopedConstraintId, Constraint<'db>>,
all_visibility_constraints: &IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
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<ScopedConstraintId, Constraint<'db>>,
all_visibility_constraints: &IndexVec<ScopedVisibilityConstraintId, VisibilityConstraintRef>,
all_visibility_constraints: &VisibilityConstraints,
visibility_constraint_id: ScopedVisibilityConstraintId,
max_depth: usize,
) -> Truthiness {