Skip to content

Commit 8b23086

Browse files
authored
[red-knot] Add typing.Any as a spelling for the Any type (#14742)
We already had a representation for the Any type, which we would use e.g. for expressions without type annotations. We now recognize `typing.Any` as a way to refer to this type explicitly. Like other special forms, this is tracked correctly through aliasing, and isn't confused with local definitions that happen to have the same name. Closes #14544
1 parent 948549f commit 8b23086

File tree

4 files changed

+104
-2
lines changed

4 files changed

+104
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Any
2+
3+
## Annotation
4+
5+
`typing.Any` is a way to name the Any type.
6+
7+
```py
8+
from typing import Any
9+
10+
x: Any = 1
11+
x = "foo"
12+
13+
def f():
14+
reveal_type(x) # revealed: Any
15+
```
16+
17+
## Aliased to a different name
18+
19+
If you alias `typing.Any` to another name, we still recognize that as a spelling of the Any type.
20+
21+
```py
22+
from typing import Any as RenamedAny
23+
24+
x: RenamedAny = 1
25+
x = "foo"
26+
27+
def f():
28+
reveal_type(x) # revealed: Any
29+
```
30+
31+
## Shadowed class
32+
33+
If you define your own class named `Any`, using that in a type expression refers to your class, and
34+
isn't a spelling of the Any type.
35+
36+
```py
37+
class Any:
38+
pass
39+
40+
x: Any
41+
42+
def f():
43+
reveal_type(x) # revealed: Any
44+
45+
# This verifies that we're not accidentally seeing typing.Any, since str is assignable
46+
# to that but not to our locally defined class.
47+
y: Any = "not an Any" # error: [invalid-assignment]
48+
```
49+
50+
## Subclass
51+
52+
The spec allows you to define subclasses of `Any`.
53+
54+
TODO: Handle assignments correctly. `Subclass` has an unknown superclass, which might be `int`. The
55+
assignment to `x` should not be allowed, even when the unknown superclass is `int`. The assignment
56+
to `y` should be allowed, since `Subclass` might have `int` as a superclass, and is therefore
57+
assignable to `int`.
58+
59+
```py
60+
from typing import Any
61+
62+
class Subclass(Any):
63+
pass
64+
65+
reveal_type(Subclass.__mro__) # revealed: tuple[Literal[Subclass], Any, Literal[object]]
66+
67+
x: Subclass = 1 # error: [invalid-assignment]
68+
# TODO: no diagnostic
69+
y: int = Subclass() # error: [invalid-assignment]
70+
71+
def f() -> Subclass:
72+
pass
73+
74+
reveal_type(f()) # revealed: Subclass
75+
```

crates/red_knot_python_semantic/src/types.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,7 @@ impl<'db> Type<'db> {
15601560
Type::Never
15611561
}
15621562
Type::KnownInstance(KnownInstanceType::LiteralString) => Type::LiteralString,
1563+
Type::KnownInstance(KnownInstanceType::Any) => Type::Any,
15631564
_ => todo_type!(),
15641565
}
15651566
}
@@ -1902,6 +1903,8 @@ pub enum KnownInstanceType<'db> {
19021903
NoReturn,
19031904
/// The symbol `typing.Never` available since 3.11 (which can also be found as `typing_extensions.Never`)
19041905
Never,
1906+
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
1907+
Any,
19051908
/// A single instance of `typing.TypeVar`
19061909
TypeVar(TypeVarInstance<'db>),
19071910
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
@@ -1919,6 +1922,7 @@ impl<'db> KnownInstanceType<'db> {
19191922
Self::TypeVar(_) => "TypeVar",
19201923
Self::NoReturn => "NoReturn",
19211924
Self::Never => "Never",
1925+
Self::Any => "Any",
19221926
Self::TypeAliasType(_) => "TypeAliasType",
19231927
}
19241928
}
@@ -1933,6 +1937,7 @@ impl<'db> KnownInstanceType<'db> {
19331937
| Self::Union
19341938
| Self::NoReturn
19351939
| Self::Never
1940+
| Self::Any
19361941
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
19371942
}
19381943
}
@@ -1946,6 +1951,7 @@ impl<'db> KnownInstanceType<'db> {
19461951
Self::Union => "typing.Union",
19471952
Self::NoReturn => "typing.NoReturn",
19481953
Self::Never => "typing.Never",
1954+
Self::Any => "typing.Any",
19491955
Self::TypeVar(typevar) => typevar.name(db),
19501956
Self::TypeAliasType(_) => "typing.TypeAliasType",
19511957
}
@@ -1960,6 +1966,7 @@ impl<'db> KnownInstanceType<'db> {
19601966
Self::Union => KnownClass::SpecialForm,
19611967
Self::NoReturn => KnownClass::SpecialForm,
19621968
Self::Never => KnownClass::SpecialForm,
1969+
Self::Any => KnownClass::Object,
19631970
Self::TypeVar(_) => KnownClass::TypeVar,
19641971
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
19651972
}
@@ -1979,6 +1986,7 @@ impl<'db> KnownInstanceType<'db> {
19791986
return None;
19801987
}
19811988
match (module.name().as_str(), instance_name) {
1989+
("typing", "Any") => Some(Self::Any),
19821990
("typing" | "typing_extensions", "Literal") => Some(Self::Literal),
19831991
("typing" | "typing_extensions", "LiteralString") => Some(Self::LiteralString),
19841992
("typing" | "typing_extensions", "Optional") => Some(Self::Optional),
@@ -2647,7 +2655,11 @@ impl<'db> Class<'db> {
26472655
pub fn is_subclass_of(self, db: &'db dyn Db, other: Class) -> bool {
26482656
// `is_subclass_of` is checking the subtype relation, in which gradual types do not
26492657
// participate, so we should not return `True` if we find `Any/Unknown` in the MRO.
2650-
self.iter_mro(db).contains(&ClassBase::Class(other))
2658+
self.is_subclass_of_base(db, other)
2659+
}
2660+
2661+
fn is_subclass_of_base(self, db: &'db dyn Db, other: impl Into<ClassBase<'db>>) -> bool {
2662+
self.iter_mro(db).contains(&other.into())
26512663
}
26522664

26532665
/// Return the explicit `metaclass` of this class, if one is defined.

crates/red_knot_python_semantic/src/types/infer.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1660,7 +1660,7 @@ impl<'db> TypeInferenceBuilder<'db> {
16601660
let value_ty = self.expression_ty(value);
16611661
let name_ast_id = name.scoped_expression_id(self.db, self.scope());
16621662

1663-
let target_ty = match assignment.target() {
1663+
let mut target_ty = match assignment.target() {
16641664
TargetKind::Sequence(unpack) => {
16651665
let unpacked = infer_unpack_types(self.db, unpack);
16661666
// Only copy the diagnostics if this is the first assignment to avoid duplicating the
@@ -1674,6 +1674,13 @@ impl<'db> TypeInferenceBuilder<'db> {
16741674
TargetKind::Name => value_ty,
16751675
};
16761676

1677+
if let Some(known_instance) = file_to_module(self.db, definition.file(self.db))
1678+
.as_ref()
1679+
.and_then(|module| KnownInstanceType::try_from_module_and_symbol(module, &name.id))
1680+
{
1681+
target_ty = Type::KnownInstance(known_instance);
1682+
}
1683+
16771684
self.store_expression_type(name, target_ty);
16781685
self.add_binding(name.into(), definition, target_ty);
16791686
}
@@ -4653,6 +4660,7 @@ impl<'db> TypeInferenceBuilder<'db> {
46534660
);
46544661
Type::Unknown
46554662
}
4663+
KnownInstanceType::Any => Type::Any,
46564664
}
46574665
}
46584666

crates/red_knot_python_semantic/src/types/mro.rs

+7
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ impl<'db> ClassBase<'db> {
379379
| KnownInstanceType::NoReturn
380380
| KnownInstanceType::Never
381381
| KnownInstanceType::Optional => None,
382+
KnownInstanceType::Any => Some(Self::Any),
382383
},
383384
}
384385
}
@@ -406,6 +407,12 @@ impl<'db> ClassBase<'db> {
406407
}
407408
}
408409

410+
impl<'db> From<Class<'db>> for ClassBase<'db> {
411+
fn from(value: Class<'db>) -> Self {
412+
ClassBase::Class(value)
413+
}
414+
}
415+
409416
impl<'db> From<ClassBase<'db>> for Type<'db> {
410417
fn from(value: ClassBase<'db>) -> Self {
411418
match value {

0 commit comments

Comments
 (0)