Skip to content

Commit 59aa869

Browse files
authored
[ty] Optimize protocol subtyping by removing expensive and unnecessary equivalence check from the top of Type::has_relation_to() (#19230)
1 parent edaffa6 commit 59aa869

File tree

2 files changed

+22
-15
lines changed

2 files changed

+22
-15
lines changed

crates/ty_python_semantic/src/types.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,10 +1215,11 @@ impl<'db> Type<'db> {
12151215

12161216
fn has_relation_to(self, db: &'db dyn Db, target: Type<'db>, relation: TypeRelation) -> bool {
12171217
// Subtyping implies assignability, so if subtyping is reflexive and the two types are
1218-
// equivalent, it is both a subtype and assignable. Assignability is always reflexive.
1219-
if (relation.is_assignability() || self.subtyping_is_always_reflexive())
1220-
&& self.is_equivalent_to(db, target)
1221-
{
1218+
// equal, it is both a subtype and assignable. Assignability is always reflexive.
1219+
//
1220+
// Note that we could do a full equivalence check here, but that would be both expensive
1221+
// and unnecessary. This early return is only an optimisation.
1222+
if (relation.is_assignability() || self.subtyping_is_always_reflexive()) && self == target {
12221223
return true;
12231224
}
12241225

@@ -1256,6 +1257,9 @@ impl<'db> Type<'db> {
12561257

12571258
// Two identical typevars must always solve to the same type, so they are always
12581259
// subtypes of each other and assignable to each other.
1260+
//
1261+
// Note that this is not handled by the early return at the beginning of this method,
1262+
// since subtyping between a TypeVar and an arbitrary other type cannot be guaranteed to be reflexive.
12591263
(Type::TypeVar(lhs_typevar), Type::TypeVar(rhs_typevar))
12601264
if lhs_typevar == rhs_typevar =>
12611265
{
@@ -7906,17 +7910,17 @@ impl<'db> UnionType<'db> {
79067910

79077911
/// Return `true` if `self` represents the exact same sets of possible runtime objects as `other`
79087912
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
7913+
if self == other {
7914+
return true;
7915+
}
7916+
79097917
let self_elements = self.elements(db);
79107918
let other_elements = other.elements(db);
79117919

79127920
if self_elements.len() != other_elements.len() {
79137921
return false;
79147922
}
79157923

7916-
if self == other {
7917-
return true;
7918-
}
7919-
79207924
let sorted_self = self.normalized(db);
79217925

79227926
if sorted_self == other {
@@ -7997,26 +8001,24 @@ impl<'db> IntersectionType<'db> {
79978001

79988002
/// Return `true` if `self` represents exactly the same set of possible runtime objects as `other`
79998003
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
8000-
let self_positive = self.positive(db);
8004+
if self == other {
8005+
return true;
8006+
}
80018007

8008+
let self_positive = self.positive(db);
80028009
let other_positive = other.positive(db);
80038010

80048011
if self_positive.len() != other_positive.len() {
80058012
return false;
80068013
}
80078014

80088015
let self_negative = self.negative(db);
8009-
80108016
let other_negative = other.negative(db);
80118017

80128018
if self_negative.len() != other_negative.len() {
80138019
return false;
80148020
}
80158021

8016-
if self == other {
8017-
return true;
8018-
}
8019-
80208022
let sorted_self = self.normalized(db);
80218023

80228024
if sorted_self == other {

crates/ty_python_semantic/src/types/class.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,13 @@ impl<'db> ClassType<'db> {
443443
}
444444

445445
pub(super) fn is_equivalent_to(self, db: &'db dyn Db, other: ClassType<'db>) -> bool {
446+
if self == other {
447+
return true;
448+
}
449+
446450
match (self, other) {
447-
(ClassType::NonGeneric(this), ClassType::NonGeneric(other)) => this == other,
451+
// A non-generic class is never equivalent to a generic class.
452+
// Two non-generic classes are only equivalent if they are equal (handled above).
448453
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => false,
449454

450455
(ClassType::Generic(this), ClassType::Generic(other)) => {

0 commit comments

Comments
 (0)