mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] Optimize protocol subtyping by removing expensive and unnecessary equivalence check from the top of Type::has_relation_to()
(#19230)
This commit is contained in:
parent
edaffa6c4f
commit
59aa869724
2 changed files with 22 additions and 15 deletions
|
@ -1215,10 +1215,11 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
fn has_relation_to(self, db: &'db dyn Db, target: Type<'db>, relation: TypeRelation) -> bool {
|
fn has_relation_to(self, db: &'db dyn Db, target: Type<'db>, relation: TypeRelation) -> bool {
|
||||||
// Subtyping implies assignability, so if subtyping is reflexive and the two types are
|
// Subtyping implies assignability, so if subtyping is reflexive and the two types are
|
||||||
// equivalent, it is both a subtype and assignable. Assignability is always reflexive.
|
// equal, it is both a subtype and assignable. Assignability is always reflexive.
|
||||||
if (relation.is_assignability() || self.subtyping_is_always_reflexive())
|
//
|
||||||
&& self.is_equivalent_to(db, target)
|
// Note that we could do a full equivalence check here, but that would be both expensive
|
||||||
{
|
// and unnecessary. This early return is only an optimisation.
|
||||||
|
if (relation.is_assignability() || self.subtyping_is_always_reflexive()) && self == target {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,6 +1257,9 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
// Two identical typevars must always solve to the same type, so they are always
|
// Two identical typevars must always solve to the same type, so they are always
|
||||||
// subtypes of each other and assignable to each other.
|
// subtypes of each other and assignable to each other.
|
||||||
|
//
|
||||||
|
// Note that this is not handled by the early return at the beginning of this method,
|
||||||
|
// since subtyping between a TypeVar and an arbitrary other type cannot be guaranteed to be reflexive.
|
||||||
(Type::TypeVar(lhs_typevar), Type::TypeVar(rhs_typevar))
|
(Type::TypeVar(lhs_typevar), Type::TypeVar(rhs_typevar))
|
||||||
if lhs_typevar == rhs_typevar =>
|
if lhs_typevar == rhs_typevar =>
|
||||||
{
|
{
|
||||||
|
@ -7906,6 +7910,10 @@ impl<'db> UnionType<'db> {
|
||||||
|
|
||||||
/// Return `true` if `self` represents the exact same sets of possible runtime objects as `other`
|
/// Return `true` if `self` represents the exact same sets of possible runtime objects as `other`
|
||||||
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let self_elements = self.elements(db);
|
let self_elements = self.elements(db);
|
||||||
let other_elements = other.elements(db);
|
let other_elements = other.elements(db);
|
||||||
|
|
||||||
|
@ -7913,10 +7921,6 @@ impl<'db> UnionType<'db> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self == other {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sorted_self = self.normalized(db);
|
let sorted_self = self.normalized(db);
|
||||||
|
|
||||||
if sorted_self == other {
|
if sorted_self == other {
|
||||||
|
@ -7997,8 +8001,11 @@ impl<'db> IntersectionType<'db> {
|
||||||
|
|
||||||
/// Return `true` if `self` represents exactly the same set of possible runtime objects as `other`
|
/// Return `true` if `self` represents exactly the same set of possible runtime objects as `other`
|
||||||
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
let self_positive = self.positive(db);
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let self_positive = self.positive(db);
|
||||||
let other_positive = other.positive(db);
|
let other_positive = other.positive(db);
|
||||||
|
|
||||||
if self_positive.len() != other_positive.len() {
|
if self_positive.len() != other_positive.len() {
|
||||||
|
@ -8006,17 +8013,12 @@ impl<'db> IntersectionType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_negative = self.negative(db);
|
let self_negative = self.negative(db);
|
||||||
|
|
||||||
let other_negative = other.negative(db);
|
let other_negative = other.negative(db);
|
||||||
|
|
||||||
if self_negative.len() != other_negative.len() {
|
if self_negative.len() != other_negative.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self == other {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sorted_self = self.normalized(db);
|
let sorted_self = self.normalized(db);
|
||||||
|
|
||||||
if sorted_self == other {
|
if sorted_self == other {
|
||||||
|
|
|
@ -443,8 +443,13 @@ impl<'db> ClassType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn is_equivalent_to(self, db: &'db dyn Db, other: ClassType<'db>) -> bool {
|
pub(super) fn is_equivalent_to(self, db: &'db dyn Db, other: ClassType<'db>) -> bool {
|
||||||
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(ClassType::NonGeneric(this), ClassType::NonGeneric(other)) => this == other,
|
// A non-generic class is never equivalent to a generic class.
|
||||||
|
// Two non-generic classes are only equivalent if they are equal (handled above).
|
||||||
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => false,
|
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => false,
|
||||||
|
|
||||||
(ClassType::Generic(this), ClassType::Generic(other)) => {
|
(ClassType::Generic(this), ClassType::Generic(other)) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue