Skip to content

Commit

Permalink
[red-knot] ClassLiteral(<T>) is not a disjoint type from `Instance(…
Browse files Browse the repository at this point in the history
…<metaclass of T>)` (#14970)

## Summary

A class is an instance of its metaclass, so `ClassLiteral("ABC")` is not
disjoint from `Instance("ABCMeta")`. However, we erroneously consider
the two types disjoint on the `main` branch. This PR fixes that.

This bug was uncovered by adding some more core types to the property
tests that provide coverage for classes that have custom metaclasses.
The additions to the property tests are included in this PR.

## Test Plan

New unit tests and property tests added. Tested with:
- `cargo test -p red_knot_python_semantic`
- `QUICKCHECK_TESTS=100000 cargo test -p red_knot_python_semantic --
--ignored types::property_tests::stable`

The assignability property test fails on this branch, but that's a known
issue that exists on `main`, due to
#14899.
  • Loading branch information
AlexWaygood authored Dec 14, 2024
1 parent ac31b26 commit 4d64cdb
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 4 deletions.
21 changes: 17 additions & 4 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,10 +1157,15 @@ impl<'db> Type<'db> {
Some(KnownClass::Slice | KnownClass::Object)
),

(Type::ClassLiteral(..), Type::Instance(InstanceType { class }))
| (Type::Instance(InstanceType { class }), Type::ClassLiteral(..)) => {
!matches!(class.known(db), Some(KnownClass::Type | KnownClass::Object))
}
(
Type::ClassLiteral(ClassLiteralType { class: class_a }),
Type::Instance(InstanceType { class: class_b }),
)
| (
Type::Instance(InstanceType { class: class_b }),
Type::ClassLiteral(ClassLiteralType { class: class_a }),
) => !class_a.is_instance_of(db, class_b),

(Type::FunctionLiteral(..), Type::Instance(InstanceType { class }))
| (Type::Instance(InstanceType { class }), Type::FunctionLiteral(..)) => !matches!(
class.known(db),
Expand Down Expand Up @@ -3352,6 +3357,7 @@ pub(crate) mod tests {
Tuple(Vec<Ty>),
SubclassOfAny,
SubclassOfBuiltinClass(&'static str),
SubclassOfAbcClass(&'static str),
StdlibModule(CoreStdlibModule),
SliceLiteral(i32, i32, i32),
}
Expand Down Expand Up @@ -3404,6 +3410,12 @@ pub(crate) mod tests {
.expect_class_literal()
.class,
),
Ty::SubclassOfAbcClass(s) => Type::subclass_of(
core_module_symbol(db, CoreStdlibModule::Abc, s)
.expect_type()
.expect_class_literal()
.class,
),
Ty::StdlibModule(module) => {
Type::ModuleLiteral(resolve_module(db, &module.name()).unwrap().file())
}
Expand Down Expand Up @@ -3744,6 +3756,7 @@ pub(crate) mod tests {
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Tuple(vec![Ty::IntLiteral(1), Ty::BuiltinInstance("int")]))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::BuiltinInstance("type"))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::SubclassOfAny)]
#[test_case(Ty::AbcClassLiteral("ABC"), Ty::AbcInstance("ABCMeta"))]
fn is_not_disjoint_from(a: Ty, b: Ty) {
let db = setup_db();
let a = a.into_type(&db);
Expand Down
7 changes: 7 additions & 0 deletions crates/red_knot_python_semantic/src/types/property_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,16 @@ fn arbitrary_core_type(g: &mut Gen) -> Ty {
Ty::BuiltinClassLiteral("bool"),
Ty::BuiltinClassLiteral("object"),
Ty::BuiltinInstance("type"),
Ty::AbcInstance("ABC"),
Ty::AbcInstance("ABCMeta"),
Ty::SubclassOfAny,
Ty::SubclassOfBuiltinClass("object"),
Ty::SubclassOfBuiltinClass("str"),
Ty::SubclassOfBuiltinClass("type"),
Ty::AbcClassLiteral("ABC"),
Ty::AbcClassLiteral("ABCMeta"),
Ty::SubclassOfAbcClass("ABC"),
Ty::SubclassOfAbcClass("ABCMeta"),
])
.unwrap()
.clone()
Expand Down

0 comments on commit 4d64cdb

Please sign in to comment.