Compare commits
6 Commits
jack/loop-
...
alex/optim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
742a63d340 | ||
|
|
3feb3dfb6d | ||
|
|
5c36ab23b3 | ||
|
|
f4206d524a | ||
|
|
ff387ad74a | ||
|
|
4abaf61b50 |
@@ -1,4 +1,5 @@
|
|||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
|
use get_size2::GetSize;
|
||||||
use infer::nearest_enclosing_class;
|
use infer::nearest_enclosing_class;
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
use ruff_diagnostics::{Edit, Fix};
|
use ruff_diagnostics::{Edit, Fix};
|
||||||
@@ -14383,7 +14384,7 @@ pub struct IntersectionType<'db> {
|
|||||||
/// narrowing along with intersections (e.g. `if not isinstance(...)`), so we represent them
|
/// narrowing along with intersections (e.g. `if not isinstance(...)`), so we represent them
|
||||||
/// directly in intersections rather than as a separate type.
|
/// directly in intersections rather than as a separate type.
|
||||||
#[returns(ref)]
|
#[returns(ref)]
|
||||||
negative: FxOrderSet<Type<'db>>,
|
negative: smallvec::SmallVec<[Type<'db>; 1]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Salsa heap is tracked separately.
|
// The Salsa heap is tracked separately.
|
||||||
@@ -14423,25 +14424,22 @@ impl<'db> IntersectionType<'db> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
fn normalized_set<'db>(
|
let mut positive: FxOrderSet<Type<'db>> = self
|
||||||
db: &'db dyn Db,
|
.positive(db)
|
||||||
elements: &FxOrderSet<Type<'db>>,
|
.iter()
|
||||||
visitor: &NormalizedVisitor<'db>,
|
.map(|ty| ty.normalized_impl(db, visitor))
|
||||||
) -> FxOrderSet<Type<'db>> {
|
.collect();
|
||||||
let mut elements: FxOrderSet<Type<'db>> = elements
|
|
||||||
.iter()
|
|
||||||
.map(|ty| ty.normalized_impl(db, visitor))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
let mut negative: smallvec::SmallVec<[Type<'db>; 1]> = self
|
||||||
elements
|
.negative(db)
|
||||||
}
|
.iter()
|
||||||
|
.map(|n| n.normalized_impl(db, visitor))
|
||||||
|
.collect();
|
||||||
|
|
||||||
IntersectionType::new(
|
positive.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||||
db,
|
negative.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||||
normalized_set(db, self.positive(db), visitor),
|
|
||||||
normalized_set(db, self.negative(db), visitor),
|
IntersectionType::new(db, positive, negative)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn recursive_type_normalized_impl(
|
pub(crate) fn recursive_type_normalized_impl(
|
||||||
@@ -14450,42 +14448,34 @@ impl<'db> IntersectionType<'db> {
|
|||||||
div: Type<'db>,
|
div: Type<'db>,
|
||||||
nested: bool,
|
nested: bool,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
fn opt_normalized_set<'db>(
|
let positive: FxOrderSet<Type<'db>> = if nested {
|
||||||
db: &'db dyn Db,
|
self.positive(db)
|
||||||
elements: &FxOrderSet<Type<'db>>,
|
|
||||||
div: Type<'db>,
|
|
||||||
nested: bool,
|
|
||||||
) -> Option<FxOrderSet<Type<'db>>> {
|
|
||||||
elements
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.recursive_type_normalized_impl(db, div, nested))
|
.map(|ty| ty.recursive_type_normalized_impl(db, div, nested))
|
||||||
.collect()
|
.collect::<Option<_>>()?
|
||||||
}
|
} else {
|
||||||
|
self.positive(db)
|
||||||
fn normalized_set<'db>(
|
|
||||||
db: &'db dyn Db,
|
|
||||||
elements: &FxOrderSet<Type<'db>>,
|
|
||||||
div: Type<'db>,
|
|
||||||
nested: bool,
|
|
||||||
) -> FxOrderSet<Type<'db>> {
|
|
||||||
elements
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| {
|
.map(|ty| {
|
||||||
ty.recursive_type_normalized_impl(db, div, nested)
|
ty.recursive_type_normalized_impl(db, div, nested)
|
||||||
.unwrap_or(div)
|
.unwrap_or(div)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
|
||||||
|
|
||||||
let positive = if nested {
|
|
||||||
opt_normalized_set(db, self.positive(db), div, nested)?
|
|
||||||
} else {
|
|
||||||
normalized_set(db, self.positive(db), div, nested)
|
|
||||||
};
|
};
|
||||||
let negative = if nested {
|
|
||||||
opt_normalized_set(db, self.negative(db), div, nested)?
|
let negative: smallvec::SmallVec<[Type<'db>; 1]> = if nested {
|
||||||
|
self.positive(db)
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.recursive_type_normalized_impl(db, div, nested))
|
||||||
|
.collect::<Option<_>>()?
|
||||||
} else {
|
} else {
|
||||||
normalized_set(db, self.negative(db), div, nested)
|
self.positive(db)
|
||||||
|
.iter()
|
||||||
|
.map(|ty| {
|
||||||
|
ty.recursive_type_normalized_impl(db, div, nested)
|
||||||
|
.unwrap_or(div)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(IntersectionType::new(db, positive, negative))
|
Some(IntersectionType::new(db, positive, negative))
|
||||||
@@ -14661,9 +14651,10 @@ impl<'db> IntersectionType<'db> {
|
|||||||
self.positive(db).is_empty() && self.negative(db).len() == 1
|
self.positive(db).is_empty() && self.negative(db).len() == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn heap_size((positive, negative): &(FxOrderSet<Type<'db>>, FxOrderSet<Type<'db>>)) -> usize {
|
fn heap_size(
|
||||||
ruff_memory_usage::order_set_heap_size(positive)
|
(positive, negative): &(FxOrderSet<Type<'db>>, smallvec::SmallVec<[Type<'db>; 1]>),
|
||||||
+ ruff_memory_usage::order_set_heap_size(negative)
|
) -> usize {
|
||||||
|
positive.get_heap_size() + negative.get_heap_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -960,7 +960,7 @@ impl<'db> IntersectionBuilder<'db> {
|
|||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct InnerIntersectionBuilder<'db> {
|
struct InnerIntersectionBuilder<'db> {
|
||||||
positive: FxOrderSet<Type<'db>>,
|
positive: FxOrderSet<Type<'db>>,
|
||||||
negative: FxOrderSet<Type<'db>>,
|
negative: NegativeIntersectionElementsBuilder<'db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> InnerIntersectionBuilder<'db> {
|
impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
@@ -1325,19 +1325,146 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||||||
(1, 0) => self.positive[0],
|
(1, 0) => self.positive[0],
|
||||||
_ => {
|
_ => {
|
||||||
self.positive.shrink_to_fit();
|
self.positive.shrink_to_fit();
|
||||||
self.negative.shrink_to_fit();
|
|
||||||
if order_elements {
|
if order_elements {
|
||||||
self.positive
|
self.positive
|
||||||
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||||
self.negative
|
self.negative
|
||||||
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||||
}
|
}
|
||||||
Type::Intersection(IntersectionType::new(db, self.positive, self.negative))
|
Type::Intersection(IntersectionType::new(
|
||||||
|
db,
|
||||||
|
self.positive,
|
||||||
|
self.negative
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.collect::<smallvec::SmallVec<_>>(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// To avoid unnecessary allocations for the common case of 0-1 negative elements,
|
||||||
|
/// we use this enum to represent the negative elements of an intersection type.
|
||||||
|
///
|
||||||
|
/// It should otherwise have identical behavior to `FxOrderSet<Type<'db>>`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize, salsa::Update, Default)]
|
||||||
|
pub enum NegativeIntersectionElementsBuilder<'db> {
|
||||||
|
#[default]
|
||||||
|
Empty,
|
||||||
|
Single(Type<'db>),
|
||||||
|
Multiple(FxOrderSet<Type<'db>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> NegativeIntersectionElementsBuilder<'db> {
|
||||||
|
pub(crate) fn iter(&self) -> NegativeIntersectionElementsIterator<'_, 'db> {
|
||||||
|
match self {
|
||||||
|
Self::Empty => NegativeIntersectionElementsIterator::EmptyOrOne(None),
|
||||||
|
Self::Single(ty) => NegativeIntersectionElementsIterator::EmptyOrOne(Some(ty)),
|
||||||
|
Self::Multiple(set) => NegativeIntersectionElementsIterator::Multiple(set.iter()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Empty => 0,
|
||||||
|
Self::Single(_) => 1,
|
||||||
|
Self::Multiple(set) => set.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert(&mut self, ty: Type<'db>) {
|
||||||
|
match self {
|
||||||
|
Self::Empty => *self = Self::Single(ty),
|
||||||
|
Self::Single(existing) => {
|
||||||
|
if ty != *existing {
|
||||||
|
*self = Self::Multiple(FxOrderSet::from_iter([*existing, ty]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Multiple(set) => {
|
||||||
|
set.insert(ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sort_unstable_by(
|
||||||
|
&mut self,
|
||||||
|
compare: impl FnMut(&Type<'db>, &Type<'db>) -> std::cmp::Ordering,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
Self::Empty | Self::Single(_) => {}
|
||||||
|
Self::Multiple(set) => {
|
||||||
|
set.sort_unstable_by(compare);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn swap_remove(&mut self, ty: &Type<'db>) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Empty => false,
|
||||||
|
Self::Single(existing) => {
|
||||||
|
if existing == ty {
|
||||||
|
*self = Self::Empty;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We don't try to maintain the invariant here that length-0 collections
|
||||||
|
// are *always* `Self::Empty` and length-1 collections are *always*
|
||||||
|
// `Self::Single`. It's unnecessary to do so, and would probably add overhead.
|
||||||
|
Self::Multiple(set) => set.swap_remove(ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn swap_remove_index(&mut self, index: usize) -> Option<Type<'db>> {
|
||||||
|
match self {
|
||||||
|
Self::Empty => None,
|
||||||
|
Self::Single(existing) => {
|
||||||
|
if index == 0 {
|
||||||
|
let ty = *existing;
|
||||||
|
*self = Self::Empty;
|
||||||
|
Some(ty)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We don't try to maintain the invariant here that length-0 collections
|
||||||
|
// are *always* `Self::Empty` and length-1 collections are *always*
|
||||||
|
// `Self::Single`. It's unnecessary to do so, and would probably add overhead.
|
||||||
|
Self::Multiple(set) => set.swap_remove_index(index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'db> IntoIterator for &'a NegativeIntersectionElementsBuilder<'db> {
|
||||||
|
type Item = &'a Type<'db>;
|
||||||
|
type IntoIter = NegativeIntersectionElementsIterator<'a, 'db>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum NegativeIntersectionElementsIterator<'a, 'db> {
|
||||||
|
EmptyOrOne(Option<&'a Type<'db>>),
|
||||||
|
Multiple(ordermap::set::Iter<'a, Type<'db>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'db> Iterator for NegativeIntersectionElementsIterator<'a, 'db> {
|
||||||
|
type Item = &'a Type<'db>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self {
|
||||||
|
NegativeIntersectionElementsIterator::EmptyOrOne(opt) => opt.take(),
|
||||||
|
NegativeIntersectionElementsIterator::Multiple(iter) => iter.next(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::iter::FusedIterator for NegativeIntersectionElementsIterator<'_, '_> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{IntersectionBuilder, Type, UnionBuilder, UnionType};
|
use super::{IntersectionBuilder, Type, UnionBuilder, UnionType};
|
||||||
|
|||||||
Reference in New Issue
Block a user