[ty] Fix disjointness checks with type-of @final classes (#21770)
## Summary We currently perform a subtyping check, similar to what we were doing for `@final` instances before https://github.com/astral-sh/ruff/pull/21167, which is incorrect, e.g. we currently consider `type[X[Any]]` and `type[X[T]]]` disjoint (where `X` is `@final`).
This commit is contained in:
@@ -3353,7 +3353,6 @@ impl<'db> Type<'db> {
|
||||
| Type::WrapperDescriptor(..)
|
||||
| Type::ModuleLiteral(..)
|
||||
| Type::ClassLiteral(..)
|
||||
| Type::GenericAlias(..)
|
||||
| Type::SpecialForm(..)
|
||||
| Type::KnownInstance(..)),
|
||||
right @ (Type::BooleanLiteral(..)
|
||||
@@ -3367,7 +3366,6 @@ impl<'db> Type<'db> {
|
||||
| Type::WrapperDescriptor(..)
|
||||
| Type::ModuleLiteral(..)
|
||||
| Type::ClassLiteral(..)
|
||||
| Type::GenericAlias(..)
|
||||
| Type::SpecialForm(..)
|
||||
| Type::KnownInstance(..)),
|
||||
) => ConstraintSet::from(left != right),
|
||||
@@ -3550,13 +3548,39 @@ impl<'db> Type<'db> {
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
|
||||
(Type::GenericAlias(left_alias), Type::GenericAlias(right_alias)) => {
|
||||
ConstraintSet::from(left_alias.origin(db) != right_alias.origin(db)).or(db, || {
|
||||
left_alias.specialization(db).is_disjoint_from_impl(
|
||||
db,
|
||||
right_alias.specialization(db),
|
||||
inferable,
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
(Type::ClassLiteral(class_literal), other @ Type::GenericAlias(_))
|
||||
| (other @ Type::GenericAlias(_), Type::ClassLiteral(class_literal)) => class_literal
|
||||
.default_specialization(db)
|
||||
.into_generic_alias()
|
||||
.when_none_or(|alias| {
|
||||
other.is_disjoint_from_impl(
|
||||
db,
|
||||
Type::GenericAlias(alias),
|
||||
inferable,
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
)
|
||||
}),
|
||||
|
||||
(Type::SubclassOf(subclass_of_ty), Type::ClassLiteral(class_b))
|
||||
| (Type::ClassLiteral(class_b), Type::SubclassOf(subclass_of_ty)) => {
|
||||
match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from(false),
|
||||
SubclassOfInner::Class(class_a) => {
|
||||
class_b.when_subclass_of(db, None, class_a).negate(db)
|
||||
}
|
||||
SubclassOfInner::Class(class_a) => ConstraintSet::from(
|
||||
!class_a.could_exist_in_mro_of(db, ClassType::NonGeneric(class_b)),
|
||||
),
|
||||
SubclassOfInner::TypeVar(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -3565,9 +3589,9 @@ impl<'db> Type<'db> {
|
||||
| (Type::GenericAlias(alias_b), Type::SubclassOf(subclass_of_ty)) => {
|
||||
match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from(false),
|
||||
SubclassOfInner::Class(class_a) => ClassType::from(alias_b)
|
||||
.when_subclass_of(db, class_a, inferable)
|
||||
.negate(db),
|
||||
SubclassOfInner::Class(class_a) => ConstraintSet::from(
|
||||
!class_a.could_exist_in_mro_of(db, ClassType::Generic(alias_b)),
|
||||
),
|
||||
SubclassOfInner::TypeVar(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -3861,6 +3885,8 @@ impl<'db> Type<'db> {
|
||||
relation_visitor,
|
||||
)
|
||||
}
|
||||
|
||||
(Type::GenericAlias(_), _) | (_, Type::GenericAlias(_)) => ConstraintSet::from(true),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1911,15 +1911,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
.contains(&ClassBase::Class(other))
|
||||
}
|
||||
|
||||
pub(super) fn when_subclass_of(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: Option<Specialization<'db>>,
|
||||
other: ClassType<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
ConstraintSet::from(self.is_subclass_of(db, specialization, other))
|
||||
}
|
||||
|
||||
/// Return `true` if this class constitutes a typed dict specification (inherits from
|
||||
/// `typing.TypedDict`, either directly or indirectly).
|
||||
#[salsa::tracked(cycle_initial=is_typed_dict_cycle_initial,
|
||||
|
||||
Reference in New Issue
Block a user