Skip to content

Commit 7c953a8

Browse files
committed
[red-knot] Make is_subtype_of exhaustive
1 parent 71239f2 commit 7c953a8

File tree

1 file changed

+194
-93
lines changed
  • crates/red_knot_python_semantic/src

1 file changed

+194
-93
lines changed

crates/red_knot_python_semantic/src/types.rs

Lines changed: 194 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -605,104 +605,50 @@ impl<'db> Type<'db> {
605605
///
606606
/// [subtype of]: https://typing.readthedocs.io/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
607607
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> bool {
608+
// Two equivalent types are always equal to each other.
609+
//
610+
// "Equivalent to" here means that the two types are both fully static
611+
// and can be determined to point to exactly the same set of possible runtime objects.
612+
// For example, `int` is a subtype of `int` because `int` and `int` are equivalent to each other.
613+
// Equally, `type[object]` is a subtype of `type`,
614+
// because the former type expresses "all subclasses of `object`"
615+
// while the latter expresses "all instances of `type`",
616+
// and these are exactly the same set of objects at runtime.
608617
if self.is_equivalent_to(db, target) {
609618
return true;
610619
}
620+
621+
// Non-fully-static types do not participate in subtyping.
622+
//
623+
// Type `A` can only be a subtype of type `B` if the set of possible runtime objects
624+
// that `A` represents is a subset of the set of possible runtime objects that `B` represents.
625+
// But the set of objects that a fully static type represents is (either partially or wholly) unknown,
626+
// so the question is simply unanswerable for fully static types.
611627
if !self.is_fully_static(db) || !target.is_fully_static(db) {
612628
return false;
613629
}
630+
614631
match (self, target) {
632+
// We should have handled these immediately above.
633+
(Type::Any | Type::Unknown | Type::Todo(_), _)
634+
| (_, Type::Any | Type::Unknown | Type::Todo(_)) => {
635+
unreachable!("Non-fully-static types do not participate in subtyping!")
636+
}
637+
638+
// `Never` is the bottom type, the empty set.
639+
// It is a subtype of all other fully static types.
640+
// No other fully static type is a subtype of `Never`.
615641
(Type::Never, _) => true,
616642
(_, Type::Never) => false,
617-
(_, Type::Instance(InstanceType { class }))
618-
if class.is_known(db, KnownClass::Object) =>
619-
{
620-
true
621-
}
622-
(Type::Instance(InstanceType { class }), _)
623-
if class.is_known(db, KnownClass::Object) =>
624-
{
625-
false
626-
}
627-
(Type::BooleanLiteral(_), Type::Instance(InstanceType { class }))
628-
if matches!(class.known(db), Some(KnownClass::Bool | KnownClass::Int)) =>
629-
{
630-
true
631-
}
632-
(Type::IntLiteral(_), Type::Instance(InstanceType { class }))
633-
if class.is_known(db, KnownClass::Int) =>
634-
{
635-
true
636-
}
637-
(Type::StringLiteral(_), Type::LiteralString) => true,
638-
(
639-
Type::StringLiteral(_) | Type::LiteralString,
640-
Type::Instance(InstanceType { class }),
641-
) if class.is_known(db, KnownClass::Str) => true,
642-
(Type::BytesLiteral(_), Type::Instance(InstanceType { class }))
643-
if class.is_known(db, KnownClass::Bytes) =>
644-
{
645-
true
646-
}
647-
(Type::Tuple(self_tuple), Type::Tuple(target_tuple)) => {
648-
let self_elements = self_tuple.elements(db);
649-
let target_elements = target_tuple.elements(db);
650-
self_elements.len() == target_elements.len()
651-
&& self_elements.iter().zip(target_elements).all(
652-
|(self_element, target_element)| {
653-
self_element.is_subtype_of(db, *target_element)
654-
},
655-
)
656-
}
657-
(
658-
Type::ClassLiteral(ClassLiteralType { class: self_class }),
659-
Type::SubclassOf(SubclassOfType {
660-
base: ClassBase::Class(target_class),
661-
}),
662-
) => self_class.is_subclass_of(db, target_class),
663-
(
664-
Type::Instance(_),
665-
Type::SubclassOf(SubclassOfType {
666-
base: ClassBase::Class(target_class),
667-
}),
668-
) if target_class.is_known(db, KnownClass::Object) => {
669-
self.is_subtype_of(db, KnownClass::Type.to_instance(db))
670-
}
671-
(
672-
Type::SubclassOf(SubclassOfType {
673-
base: ClassBase::Class(self_class),
674-
}),
675-
Type::SubclassOf(SubclassOfType {
676-
base: ClassBase::Class(target_class),
677-
}),
678-
) => self_class.is_subclass_of(db, target_class),
679-
// C ⊆ type
680-
// type[C] ⊆ type
681-
// Though note that this works regardless of which metaclass C has, not just for type.
682-
(
683-
Type::ClassLiteral(ClassLiteralType { class: self_class })
684-
| Type::SubclassOf(SubclassOfType {
685-
base: ClassBase::Class(self_class),
686-
}),
687-
Type::Instance(InstanceType {
688-
class: target_class,
689-
}),
690-
) if self_class
691-
.metaclass(db)
692-
.into_class_literal()
693-
.map(|meta| meta.class.is_subclass_of(db, target_class))
694-
.unwrap_or(false) =>
695-
{
696-
true
697-
}
698-
(Type::Union(union), ty) => union
643+
644+
(Type::Union(union), _) => union
699645
.elements(db)
700646
.iter()
701-
.all(|&elem_ty| elem_ty.is_subtype_of(db, ty)),
702-
(ty, Type::Union(union)) => union
647+
.all(|&elem_ty| elem_ty.is_subtype_of(db, target)),
648+
(_, Type::Union(union)) => union
703649
.elements(db)
704650
.iter()
705-
.any(|&elem_ty| ty.is_subtype_of(db, elem_ty)),
651+
.any(|&elem_ty| self.is_subtype_of(db, elem_ty)),
706652
(Type::Intersection(self_intersection), Type::Intersection(target_intersection)) => {
707653
// Check that all target positive values are covered in self positive values
708654
target_intersection
@@ -729,26 +675,151 @@ impl<'db> Type<'db> {
729675
})
730676
})
731677
}
732-
(Type::Intersection(intersection), ty) => intersection
678+
(Type::Intersection(intersection), _) => intersection
733679
.positive(db)
734680
.iter()
735-
.any(|&elem_ty| elem_ty.is_subtype_of(db, ty)),
736-
(ty, Type::Intersection(intersection)) => {
681+
.any(|&elem_ty| elem_ty.is_subtype_of(db, target)),
682+
(_, Type::Intersection(intersection)) => {
737683
intersection
738684
.positive(db)
739685
.iter()
740-
.all(|&pos_ty| ty.is_subtype_of(db, pos_ty))
686+
.all(|&pos_ty| self.is_subtype_of(db, pos_ty))
741687
&& intersection
742688
.negative(db)
743689
.iter()
744-
.all(|&neg_ty| neg_ty.is_disjoint_from(db, ty))
690+
.all(|&neg_ty| neg_ty.is_disjoint_from(db, self))
745691
}
692+
693+
// All `StringLiteral` types are a subtype of `LiteralString`.
694+
(Type::StringLiteral(_), Type::LiteralString) => true,
695+
696+
// Except for the special `LiteralString` case above,
697+
// most `Literal` types delegate to their instance fallbacks
698+
// unless `self` is exactly equivalent to `target` (handled above)
699+
(Type::StringLiteral(_) | Type::LiteralString, _) => {
700+
KnownClass::Str.to_instance(db).is_subtype_of(db, target)
701+
}
702+
(Type::BooleanLiteral(_), _) => {
703+
KnownClass::Bool.to_instance(db).is_subtype_of(db, target)
704+
}
705+
(Type::IntLiteral(_), _) => KnownClass::Int.to_instance(db).is_subtype_of(db, target),
706+
(Type::BytesLiteral(_), _) => {
707+
KnownClass::Bytes.to_instance(db).is_subtype_of(db, target)
708+
}
709+
(Type::ModuleLiteral(_), _) => KnownClass::ModuleType
710+
.to_instance(db)
711+
.is_subtype_of(db, target),
712+
(Type::SliceLiteral(_), _) => {
713+
KnownClass::Slice.to_instance(db).is_subtype_of(db, target)
714+
}
715+
716+
// A `FunctionLiteral` type is a single-valued type like the other literals handled above,
717+
// so it also, for now, just delegates to its instance fallback.
718+
// This will change in a way similar to the `LiteralString`/`StringLiteral()` case above
719+
// when we add support for `typing.Callable`.
720+
(Type::FunctionLiteral(_), _) => KnownClass::FunctionType
721+
.to_instance(db)
722+
.is_subtype_of(db, target),
723+
724+
// A fully static heterogenous tuple type `A` is a subtype of a fully static heterogeneous tuple type `B`
725+
// iff the two tuple types have the same number of elements and each element-type in `A` is a subtype
726+
// of the element-type at the same index in `B`. (Now say that 5 times fast.)
727+
//
728+
// For example: `tuple[bool, bool]` is a subtype of `tuple[int, int]`,
729+
// but `tuple[bool, bool, bool]` is not a subtype of `tuple[int, int]`
730+
(Type::Tuple(self_tuple), Type::Tuple(target_tuple)) => {
731+
let self_elements = self_tuple.elements(db);
732+
let target_elements = target_tuple.elements(db);
733+
self_elements.len() == target_elements.len()
734+
&& self_elements.iter().zip(target_elements).all(
735+
|(self_element, target_element)| {
736+
self_element.is_subtype_of(db, *target_element)
737+
},
738+
)
739+
}
740+
741+
// Other than the special tuple-to-tuple case handled, above,
742+
// tuple subtyping delegates to `Instance(tuple)` in the same way as the literal types.
743+
//
744+
// All heterogenous tuple types are subtypes of `Instance(<tuple>)`:
745+
// `Instance(<some class T>)` expresses "the set of all possible instances of the class `T`";
746+
// consequently, `Instance(<tuple>)` expresses "the set of all possible instances of the class `tuple`".
747+
// This type can be spelled in type annotations as `tuple[object, ...]` (since `tuple` is covariant).
748+
//
749+
// Note that this is not the same type as the type spelled in type annotations as `tuple`;
750+
// as that type is equivalent to `type[Any, ...]` (and therefore not a fully static type).
751+
(Type::Tuple(_), _) => KnownClass::Tuple.to_instance(db).is_subtype_of(db, target),
752+
753+
// `Type::ClassLiteral(T)` expresses "the set of size one with only the class `T` in it;
754+
// `Type::SubclassOf(S)` expresses "the set that contains all subclasses of the class `S`".
755+
// The first is only a subtype of the second if `T` is a subclass of `S`.
756+
//
757+
// For example, `Literal[int]` and `Literal[bool]` are both subtypes of `type[int]`.
758+
(
759+
Type::ClassLiteral(ClassLiteralType { class: self_class }),
760+
Type::SubclassOf(SubclassOfType {
761+
base: ClassBase::Class(target_class),
762+
}),
763+
) => self_class.is_subclass_of(db, target_class),
764+
765+
// `Literal[str]` is a subtype of `type` because `str` is an instance of `type`.
766+
// `Literal[enum.Enum]` is a subtype of `enum.EnumMeta` because `enum.Enum`
767+
// is an instance of `enum.EnumMeta`.
768+
(
769+
Type::ClassLiteral(self_class_ty),
770+
Type::Instance(InstanceType {
771+
class: target_class,
772+
}),
773+
) => self_class_ty.is_instance_of(db, target_class),
774+
775+
// Other than the cases enumerated above,
776+
// class literals aren't subtypes of any other types.
777+
(Type::ClassLiteral(_), _) => false,
778+
779+
// `type[T]` delegates to `Literal[T]`
780+
// when deciding if `type[T]` is a subtype of another type.
781+
(
782+
Type::SubclassOf(SubclassOfType {
783+
base: ClassBase::Class(self_class),
784+
}),
785+
_,
786+
) => Type::class_literal(self_class).is_subtype_of(db, target),
787+
788+
// As the `unreachable!()` message says, these should all be handled
789+
// right at the top of this function.
790+
(
791+
Type::SubclassOf(SubclassOfType {
792+
base: ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_),
793+
}),
794+
_,
795+
) => unreachable!(
796+
"Non-fully-static types should be handled at the top of this function!"
797+
),
798+
799+
// For example: `Type::KnownInstance(KnownInstanceType::Type)` is a subtype of `Type::Instance(_SpecialForm)`,
800+
// because the symbol `typing.Type` is an instance of `typing._SpecialForm` at runtime
746801
(Type::KnownInstance(left), right) => {
747802
left.instance_fallback(db).is_subtype_of(db, right)
748803
}
804+
805+
// For example, `Instance(ABCMeta)` is a subtype of `type[object]`,
806+
// because (since `ABCMeta` subclasses `type`) all instances of `ABCMeta` are instances of `type`,
807+
// and all instances of `type` are members of the type `type[object]`
808+
(
809+
Type::Instance(_),
810+
Type::SubclassOf(SubclassOfType {
811+
base: ClassBase::Class(target_class),
812+
}),
813+
) if target_class.is_known(db, KnownClass::Object) => {
814+
self.is_subtype_of(db, KnownClass::Type.to_instance(db))
815+
}
816+
817+
// `bool` is a subtype of `int`, because all instances of `bool` are also instances of `int`.
749818
(Type::Instance(left), Type::Instance(right)) => left.is_instance_of(db, right.class),
750-
// TODO
751-
_ => false,
819+
820+
// Other than the special cases enumerated above,
821+
// `Instance` types are never subtypes of any other variants
822+
(Type::Instance(_), _) => false,
752823
}
753824
}
754825

@@ -2965,6 +3036,18 @@ impl<'db> ClassLiteralType<'db> {
29653036
fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
29663037
self.class.class_member(db, name)
29673038
}
3039+
3040+
/// Return `true` if the singleton class object represented by this type
3041+
/// is an instance of the class `other`.
3042+
///
3043+
/// A class is an instance of its metaclass; consequently,
3044+
/// a class will only ever be an instance of another class
3045+
/// if its metaclass is a subclass of that other class.
3046+
fn is_instance_of(self, db: &'db dyn Db, other: Class<'db>) -> bool {
3047+
self.class.metaclass(db).into_class_literal().is_some_and(
3048+
|ClassLiteralType { class: metaclass }| metaclass.is_subclass_of(db, other),
3049+
)
3050+
}
29683051
}
29693052

29703053
impl<'db> From<ClassLiteralType<'db>> for Type<'db> {
@@ -3168,7 +3251,7 @@ pub(crate) mod tests {
31683251
use super::*;
31693252
use crate::db::tests::{setup_db, TestDb, TestDbBuilder};
31703253
use crate::stdlib::typing_symbol;
3171-
use crate::PythonVersion;
3254+
use crate::{resolve_module, PythonVersion};
31723255
use ruff_db::files::system_path_to_file;
31733256
use ruff_db::parsed::parsed_module;
31743257
use ruff_db::system::DbWithTestSystem;
@@ -3208,6 +3291,8 @@ pub(crate) mod tests {
32083291
Tuple(Vec<Ty>),
32093292
SubclassOfAny,
32103293
SubclassOfBuiltinClass(&'static str),
3294+
StdlibModule(CoreStdlibModule),
3295+
SliceLiteral(i32, i32, i32),
32113296
}
32123297

32133298
impl Ty {
@@ -3258,6 +3343,15 @@ pub(crate) mod tests {
32583343
.expect_class_literal()
32593344
.class,
32603345
),
3346+
Ty::StdlibModule(module) => {
3347+
Type::ModuleLiteral(resolve_module(db, &module.name()).unwrap().file())
3348+
}
3349+
Ty::SliceLiteral(start, stop, step) => Type::SliceLiteral(SliceLiteralType::new(
3350+
db,
3351+
Some(start),
3352+
Some(stop),
3353+
Some(step),
3354+
)),
32613355
}
32623356
}
32633357
}
@@ -3384,6 +3478,13 @@ pub(crate) mod tests {
33843478
#[test_case(Ty::TypingLiteral, Ty::BuiltinInstance("object"))]
33853479
#[test_case(Ty::AbcClassLiteral("ABC"), Ty::AbcInstance("ABCMeta"))]
33863480
#[test_case(Ty::AbcInstance("ABCMeta"), Ty::SubclassOfBuiltinClass("object"))]
3481+
#[test_case(Ty::Tuple(vec![Ty::BuiltinInstance("int")]), Ty::BuiltinInstance("tuple"))]
3482+
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::BuiltinInstance("type"))]
3483+
#[test_case(
3484+
Ty::StdlibModule(CoreStdlibModule::Typing),
3485+
Ty::KnownClassInstance(KnownClass::ModuleType)
3486+
)]
3487+
#[test_case(Ty::SliceLiteral(1, 2, 3), Ty::BuiltinInstance("slice"))]
33873488
fn is_subtype_of(from: Ty, to: Ty) {
33883489
let db = setup_db();
33893490
assert!(from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));

0 commit comments

Comments
 (0)