Skip to content

Commit

Permalink
[red-knot] Fix bugs relating to assignability of dynamic type[] typ…
Browse files Browse the repository at this point in the history
…es (#14972)
  • Loading branch information
AlexWaygood authored Dec 15, 2024
1 parent 53c7ef8 commit fa46ba2
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
# `type[Any]`

This file contains tests for non-fully-static `type[]` types, such as `type[Any]` and
`type[Unknown]`.

## Simple

```py
def f(x: type[Any]):
def f(x: type[Any], y: type[str]):
reveal_type(x) # revealed: type[Any]
# TODO: could be `<object.__repr__ type> & Any`
reveal_type(x.__repr__) # revealed: Any

# type[str] and type[Any] are assignable to each other
a: type[str] = x
b: type[Any] = y

class A: ...

x: type[Any] = object
Expand Down Expand Up @@ -70,3 +77,26 @@ def test(x: Any, y: SomethingUnknown):
reveal_type(y.__class__) # revealed: type[Unknown]
reveal_type(y.__class__.__class__.__class__.__class__) # revealed: type[Unknown]
```

## `type[Unknown]` has similar properties to `type[Any]`

```py
import abc
from typing import Any
from does_not_exist import SomethingUnknown # error: [unresolved-import]

has_unknown_type = SomethingUnknown.__class__
reveal_type(has_unknown_type) # revealed: type[Unknown]

def test(x: type[str], y: type[Any]):
"""Both `type[Any]` and `type[Unknown]` are assignable to all `type[]` types"""
a: type[Any] = x
b: type[str] = y
c: type[Any] = has_unknown_type
d: type[str] = has_unknown_type

def test2(a: type[Any]):
"""`type[Any]` and `type[Unknown]` are also assignable to all instances of `type` subclasses"""
b: abc.ABCMeta = a
b: abc.ABCMeta = has_unknown_type
```
22 changes: 14 additions & 8 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -854,26 +854,26 @@ impl<'db> Type<'db> {
}
(
Type::SubclassOf(SubclassOfType {
base: ClassBase::Any,
base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown,
}),
Type::SubclassOf(_),
) => true,
(
Type::SubclassOf(SubclassOfType {
base: ClassBase::Any,
base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown,
}),
Type::Instance(target),
) if target.class.is_known(db, KnownClass::Type) => true,
Type::Instance(_),
) if target.is_assignable_to(db, KnownClass::Type.to_instance(db)) => true,
(
Type::Instance(class),
Type::Instance(_),
Type::SubclassOf(SubclassOfType {
base: ClassBase::Any,
base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown,
}),
) if class.class.is_known(db, KnownClass::Type) => true,
) if self.is_assignable_to(db, KnownClass::Type.to_instance(db)) => true,
(
Type::ClassLiteral(_) | Type::SubclassOf(_),
Type::SubclassOf(SubclassOfType {
base: ClassBase::Any,
base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown,
}),
) => true,
// TODO other types containing gradual forms (e.g. generics containing Any/Unknown)
Expand Down Expand Up @@ -3356,6 +3356,7 @@ pub(crate) mod tests {
},
Tuple(Vec<Ty>),
SubclassOfAny,
SubclassOfUnknown,
SubclassOfBuiltinClass(&'static str),
SubclassOfAbcClass(&'static str),
StdlibModule(CoreStdlibModule),
Expand Down Expand Up @@ -3404,6 +3405,7 @@ pub(crate) mod tests {
Type::tuple(db, elements)
}
Ty::SubclassOfAny => Type::subclass_of_base(ClassBase::Any),
Ty::SubclassOfUnknown => Type::subclass_of_base(ClassBase::Unknown),
Ty::SubclassOfBuiltinClass(s) => Type::subclass_of(
builtins_symbol(db, s)
.expect_type()
Expand Down Expand Up @@ -3483,6 +3485,10 @@ pub(crate) mod tests {
#[test_case(Ty::BuiltinInstance("type"), Ty::SubclassOfBuiltinClass("object"))]
#[test_case(Ty::BuiltinInstance("type"), Ty::BuiltinInstance("type"))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::SubclassOfAny)]
#[test_case(Ty::SubclassOfBuiltinClass("str"), Ty::SubclassOfUnknown)]
#[test_case(Ty::SubclassOfUnknown, Ty::SubclassOfBuiltinClass("str"))]
#[test_case(Ty::SubclassOfAny, Ty::AbcInstance("ABCMeta"))]
#[test_case(Ty::SubclassOfUnknown, Ty::AbcInstance("ABCMeta"))]
fn is_assignable_to(from: Ty, to: Ty) {
let db = setup_db();
assert!(from.into_type(&db).is_assignable_to(&db, to.into_type(&db)));
Expand Down

0 comments on commit fa46ba2

Please sign in to comment.