mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
[red-knot] Consider two instance types disjoint if the underlying classes have disjoint metaclasses (#17545)
This commit is contained in:
parent
775815ef22
commit
6bdffc3cbf
2 changed files with 41 additions and 4 deletions
|
@ -56,6 +56,19 @@ static_assert(not is_disjoint_from(FinalSubclass, A))
|
|||
# ... which makes it disjoint from B1, B2:
|
||||
static_assert(is_disjoint_from(B1, FinalSubclass))
|
||||
static_assert(is_disjoint_from(B2, FinalSubclass))
|
||||
|
||||
# Instance types can also be disjoint if they have disjoint metaclasses.
|
||||
# No possible subclass of `Meta1` and `Meta2` could exist, therefore
|
||||
# no possible subclass of `UsesMeta1` and `UsesMeta2` can exist:
|
||||
class Meta1(type): ...
|
||||
class UsesMeta1(metaclass=Meta1): ...
|
||||
|
||||
@final
|
||||
class Meta2(type): ...
|
||||
|
||||
class UsesMeta2(metaclass=Meta2): ...
|
||||
|
||||
static_assert(is_disjoint_from(UsesMeta1, UsesMeta2))
|
||||
```
|
||||
|
||||
## Tuple types
|
||||
|
@ -342,8 +355,8 @@ static_assert(is_disjoint_from(Meta1, type[UsesMeta2]))
|
|||
|
||||
### `type[T]` versus `type[S]`
|
||||
|
||||
By the same token, `type[T]` is disjoint from `type[S]` if the metaclass of `T` is disjoint from the
|
||||
metaclass of `S`.
|
||||
By the same token, `type[T]` is disjoint from `type[S]` if `T` is `@final`, `S` is `@final`, or the
|
||||
metaclass of `T` is disjoint from the metaclass of `S`.
|
||||
|
||||
```py
|
||||
from typing import final
|
||||
|
@ -353,6 +366,9 @@ from knot_extensions import static_assert, is_disjoint_from
|
|||
class Meta1(type): ...
|
||||
|
||||
class Meta2(type): ...
|
||||
|
||||
static_assert(is_disjoint_from(type[Meta1], type[Meta2]))
|
||||
|
||||
class UsesMeta1(metaclass=Meta1): ...
|
||||
class UsesMeta2(metaclass=Meta2): ...
|
||||
|
||||
|
|
|
@ -43,8 +43,29 @@ impl<'db> InstanceType<'db> {
|
|||
}
|
||||
|
||||
pub(super) fn is_disjoint_from(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
(self.class.is_final(db) && !self.class.is_subclass_of(db, other.class))
|
||||
|| (other.class.is_final(db) && !other.class.is_subclass_of(db, self.class))
|
||||
if self.class.is_final(db) && !self.class.is_subclass_of(db, other.class) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if other.class.is_final(db) && !other.class.is_subclass_of(db, self.class) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check to see whether the metaclasses of `self` and `other` are disjoint.
|
||||
// Avoid this check if the metaclass of either `self` or `other` is `type`,
|
||||
// however, since we end up with infinite recursion in that case due to the fact
|
||||
// that `type` is its own metaclass (and we know that `type` cannot be disjoint
|
||||
// from any metaclass, anyway).
|
||||
let type_type = KnownClass::Type.to_instance(db);
|
||||
let self_metaclass = self.class.metaclass_instance_type(db);
|
||||
if self_metaclass == type_type {
|
||||
return false;
|
||||
}
|
||||
let other_metaclass = other.class.metaclass_instance_type(db);
|
||||
if other_metaclass == type_type {
|
||||
return false;
|
||||
}
|
||||
self_metaclass.is_disjoint_from(db, other_metaclass)
|
||||
}
|
||||
|
||||
pub(super) fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue