Skip to content

Commit 4c4587d

Browse files
committed
fix is_disjoint_from with @final classes
1 parent 735ec0c commit 4c4587d

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@ static_assert(is_disjoint_from(memoryview, Foo))
8787
static_assert(is_disjoint_from(type[memoryview], type[Foo]))
8888
```
8989

90+
## Specialized `@final` types
91+
92+
```toml
93+
[environment]
94+
python-version = "3.12"
95+
```
96+
97+
```py
98+
from typing import final
99+
from ty_extensions import static_assert, is_disjoint_from
100+
101+
@final
102+
class Foo[T]:
103+
def get(self) -> T:
104+
raise NotImplementedError
105+
106+
static_assert(not is_disjoint_from(Foo[int], Foo[str]))
107+
```
108+
90109
## "Disjoint base" builtin types
91110

92111
Most other builtins can be subclassed and can even be used in multiple inheritance. However, builtin

crates/ty_python_semantic/src/types/class.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,12 +637,17 @@ impl<'db> ClassType<'db> {
637637
return true;
638638
}
639639

640-
// Optimisation: if either class is `@final`, we only need to do one `is_subclass_of` call.
641640
if self.is_final(db) {
642-
return self.is_subclass_of(db, other);
641+
return self
642+
.iter_mro(db)
643+
.filter_map(ClassBase::into_class)
644+
.any(|class| class.class_literal(db).0 == other.class_literal(db).0);
643645
}
644646
if other.is_final(db) {
645-
return other.is_subclass_of(db, self);
647+
return other
648+
.iter_mro(db)
649+
.filter_map(ClassBase::into_class)
650+
.any(|class| class.class_literal(db).0 == self.class_literal(db).0);
646651
}
647652

648653
// Two disjoint bases can only coexist in an MRO if one is a subclass of the other.

0 commit comments

Comments
 (0)