Skip to content

Commit 9a5b6b1

Browse files
committed
[ty] Avoid UnionBuilder overhead when creating a new union from the filtered elements of an existing union
1 parent 72b678e commit 9a5b6b1

2 files changed

Lines changed: 41 additions & 61 deletions

File tree

crates/ty_python_semantic/src/types.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11910,19 +11910,15 @@ impl<'db> UnionType<'db> {
1191011910
self.try_map(db, |element| element.to_instance(db))
1191111911
}
1191211912

11913-
pub(crate) fn filter(
11914-
self,
11915-
db: &'db dyn Db,
11916-
mut f: impl FnMut(&Type<'db>) -> bool,
11917-
) -> Type<'db> {
11918-
self.elements(db)
11919-
.iter()
11920-
.filter(|ty| f(ty))
11921-
.fold(UnionBuilder::new(db), |builder, element| {
11922-
builder.add(*element)
11923-
})
11924-
.recursively_defined(self.recursively_defined(db))
11925-
.build()
11913+
pub(crate) fn filter(self, db: &'db dyn Db, f: impl FnMut(&Type<'db>) -> bool) -> Type<'db> {
11914+
let current = self.elements(db);
11915+
let new: Box<[Type<'db>]> = current.iter().copied().filter(f).collect();
11916+
match new.len() {
11917+
0 => Type::Never,
11918+
1 => new[0],
11919+
len if len == current.len() => Type::Union(self),
11920+
_ => Type::Union(UnionType::new(db, new, self.recursively_defined(db))),
11921+
}
1192611922
}
1192711923

1192811924
pub(crate) fn map_with_boundness(

crates/ty_python_semantic/src/types/narrow.rs

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,31 +1053,23 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
10531053
&& rhs_ty.is_singleton(self.db)
10541054
{
10551055
let is_positive_check = is_positive == (ops[0] == ast::CmpOp::Is);
1056-
let union_elements = union.elements(self.db);
1057-
let filtered: Vec<_> = union_elements
1058-
.iter()
1059-
.filter(|elem| {
1060-
elem.as_nominal_instance()
1061-
.and_then(|inst| inst.tuple_spec(self.db))
1062-
.and_then(|spec| spec.py_index(self.db, index).ok())
1063-
.is_none_or(|el_ty| {
1064-
if is_positive_check {
1065-
// `is X` context: keep tuples where element could be X
1066-
!el_ty.is_disjoint_from(self.db, rhs_ty)
1067-
} else {
1068-
// `is not X` context: keep tuples where element is not always X
1069-
!el_ty.is_subtype_of(self.db, rhs_ty)
1070-
}
1071-
})
1072-
})
1073-
.copied()
1074-
.collect();
1075-
if filtered.len() < union_elements.len() {
1056+
let filtered = union.filter(self.db, |elem| {
1057+
elem.as_nominal_instance()
1058+
.and_then(|inst| inst.tuple_spec(self.db))
1059+
.and_then(|spec| spec.py_index(self.db, index).ok())
1060+
.is_none_or(|el_ty| {
1061+
if is_positive_check {
1062+
// `is X` context: keep tuples where element could be X
1063+
!el_ty.is_disjoint_from(self.db, rhs_ty)
1064+
} else {
1065+
// `is not X` context: keep tuples where element is not always X
1066+
!el_ty.is_subtype_of(self.db, rhs_ty)
1067+
}
1068+
})
1069+
});
1070+
if filtered != Type::Union(union) {
10761071
let place = self.expect_place(&subscript_place_expr);
1077-
constraints.insert(
1078-
place,
1079-
NarrowingConstraint::replacement(UnionType::from_elements(self.db, filtered)),
1080-
);
1072+
constraints.insert(place, NarrowingConstraint::replacement(filtered));
10811073
}
10821074
}
10831075

@@ -1766,33 +1758,25 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
17661758
}
17671759

17681760
// Filter the union based on whether each tuple element at the index could match the rhs.
1769-
let union_elements = union.elements(self.db);
1770-
let filtered: Vec<_> = union_elements
1771-
.iter()
1772-
.filter(|elem| {
1773-
elem.as_nominal_instance()
1774-
.and_then(|inst| inst.tuple_spec(self.db))
1775-
.and_then(|spec| spec.py_index(self.db, index).ok())
1776-
.is_none_or(|el_ty| {
1777-
if constrain_with_equality {
1778-
// Keep tuples where element could be equal to rhs.
1779-
!el_ty.is_disjoint_from(self.db, rhs_type)
1780-
} else {
1781-
// Keep tuples where element is not always equal to rhs.
1782-
!el_ty.is_subtype_of(self.db, rhs_type)
1783-
}
1784-
})
1785-
})
1786-
.copied()
1787-
.collect();
1761+
let filtered = union.filter(self.db, |elem| {
1762+
elem.as_nominal_instance()
1763+
.and_then(|inst| inst.tuple_spec(self.db))
1764+
.and_then(|spec| spec.py_index(self.db, index).ok())
1765+
.is_none_or(|el_ty| {
1766+
if constrain_with_equality {
1767+
// Keep tuples where element could be equal to rhs.
1768+
!el_ty.is_disjoint_from(self.db, rhs_type)
1769+
} else {
1770+
// Keep tuples where element is not always equal to rhs.
1771+
!el_ty.is_subtype_of(self.db, rhs_type)
1772+
}
1773+
})
1774+
});
17881775

17891776
// Only create a constraint if we actually narrowed something.
1790-
if filtered.len() < union_elements.len() {
1777+
if filtered != rhs_type {
17911778
let place = self.expect_place(&subscript_place_expr);
1792-
Some((
1793-
place,
1794-
NarrowingConstraint::replacement(UnionType::from_elements(self.db, filtered)),
1795-
))
1779+
Some((place, NarrowingConstraint::replacement(filtered)))
17961780
} else {
17971781
None
17981782
}

0 commit comments

Comments
 (0)