mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +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 {
|
||||
// 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.
|
||||
if (relation.is_assignability() || self.subtyping_is_always_reflexive())
|
||||
&& self.is_equivalent_to(db, target)
|
||||
{
|
||||
// equal, it is both a subtype and assignable. Assignability is always reflexive.
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -1256,6 +1257,9 @@ impl<'db> Type<'db> {
|
|||
|
||||
// Two identical typevars must always solve to the same type, so they are always
|
||||
// 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))
|
||||
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`
|
||||
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 other_elements = other.elements(db);
|
||||
|
||||
|
@ -7913,10 +7921,6 @@ impl<'db> UnionType<'db> {
|
|||
return false;
|
||||
}
|
||||
|
||||
if self == other {
|
||||
return true;
|
||||
}
|
||||
|
||||
let sorted_self = self.normalized(db);
|
||||
|
||||
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`
|
||||
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);
|
||||
|
||||
if self_positive.len() != other_positive.len() {
|
||||
|
@ -8006,17 +8013,12 @@ impl<'db> IntersectionType<'db> {
|
|||
}
|
||||
|
||||
let self_negative = self.negative(db);
|
||||
|
||||
let other_negative = other.negative(db);
|
||||
|
||||
if self_negative.len() != other_negative.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self == other {
|
||||
return true;
|
||||
}
|
||||
|
||||
let sorted_self = self.normalized(db);
|
||||
|
||||
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 {
|
||||
if self == other {
|
||||
return true;
|
||||
}
|
||||
|
||||
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::Generic(this), ClassType::Generic(other)) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue