Skip to content

Commit 9943e95

Browse files
committed
Add red_knot.Unknown
1 parent f286bc5 commit 9943e95

File tree

5 files changed

+45
-13
lines changed

5 files changed

+45
-13
lines changed

crates/red_knot_python_semantic/resources/mdtest/type_api.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This document describes the internal `red_knot` API for manipulating types and querying their
44
properties.
55

6-
## Type operations
6+
## Type extensions
77

88
The Python language itself allows us to perform a variety of operations on types. For example, we
99
can build a union of types like `int | None`, or we can use type constructors such as `list[int]` or
@@ -68,6 +68,25 @@ class D: ...
6868
assert_true(not is_subtype_of(ABC, D))
6969
```
7070

71+
### Unknown type
72+
73+
The `Unknown` type is a special type that we use to represent actually unknown types (no
74+
annotation), as opposed to `Any` which represents an explicitly unknown type.
75+
76+
```py
77+
from red_knot import Unknown, assert_true, is_assignable_to, is_fully_static
78+
79+
assert_true(is_assignable_to(Unknown, int))
80+
assert_true(is_assignable_to(int, Unknown))
81+
82+
assert_true(not is_fully_static(Unknown))
83+
84+
x: Unknown = 1
85+
86+
def _() -> None:
87+
reveal_type(x) # revealed: Unknown
88+
```
89+
7190
## Type predicates
7291

7392
The `red_knot` module also provides predicates to test various properties of types. These are

crates/red_knot_python_semantic/src/types.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,7 @@ impl<'db> Type<'db> {
20852085
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::BareLiteral],
20862086
fallback_type: Type::Unknown,
20872087
}),
2088+
Type::KnownInstance(KnownInstanceType::RedKnotUnknown) => Ok(Type::Unknown),
20882089
Type::Todo(_) => Ok(*self),
20892090
_ => Ok(todo_type!(
20902091
"Unsupported or invalid type in a type expression"
@@ -2595,6 +2596,7 @@ pub enum KnownInstanceType<'db> {
25952596
TypeIs,
25962597
ReadOnly,
25972598
// TODO: fill this enum out with more special forms, etc.
2599+
RedKnotUnknown,
25982600
}
25992601

26002602
impl<'db> KnownInstanceType<'db> {
@@ -2633,6 +2635,7 @@ impl<'db> KnownInstanceType<'db> {
26332635
Self::ChainMap => "ChainMap",
26342636
Self::OrderedDict => "OrderedDict",
26352637
Self::ReadOnly => "ReadOnly",
2638+
Self::RedKnotUnknown => "Unknown",
26362639
}
26372640
}
26382641

@@ -2671,7 +2674,8 @@ impl<'db> KnownInstanceType<'db> {
26712674
| Self::ChainMap
26722675
| Self::OrderedDict
26732676
| Self::ReadOnly
2674-
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
2677+
| Self::TypeAliasType(_)
2678+
| Self::RedKnotUnknown => Truthiness::AlwaysTrue,
26752679
}
26762680
}
26772681

@@ -2711,6 +2715,7 @@ impl<'db> KnownInstanceType<'db> {
27112715
Self::ReadOnly => "typing.ReadOnly",
27122716
Self::TypeVar(typevar) => typevar.name(db),
27132717
Self::TypeAliasType(_) => "typing.TypeAliasType",
2718+
Self::RedKnotUnknown => "red_knot.Unknown",
27142719
}
27152720
}
27162721

@@ -2750,6 +2755,7 @@ impl<'db> KnownInstanceType<'db> {
27502755
Self::OrderedDict => KnownClass::StdlibAlias,
27512756
Self::TypeVar(_) => KnownClass::TypeVar,
27522757
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
2758+
Self::RedKnotUnknown => KnownClass::Object,
27532759
}
27542760
}
27552761

@@ -2795,6 +2801,7 @@ impl<'db> KnownInstanceType<'db> {
27952801
"Concatenate" => Self::Concatenate,
27962802
"NotRequired" => Self::NotRequired,
27972803
"LiteralString" => Self::LiteralString,
2804+
"Unknown" => Self::RedKnotUnknown,
27982805
_ => return None,
27992806
};
28002807

@@ -2844,6 +2851,7 @@ impl<'db> KnownInstanceType<'db> {
28442851
| Self::TypeVar(_) => {
28452852
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
28462853
}
2854+
Self::RedKnotUnknown => matches!(module, KnownModule::RedKnot),
28472855
}
28482856
}
28492857

crates/red_knot_python_semantic/src/types/class_base.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ impl<'db> ClassBase<'db> {
9393
| KnownInstanceType::Required
9494
| KnownInstanceType::TypeAlias
9595
| KnownInstanceType::ReadOnly
96-
| KnownInstanceType::Optional => None,
96+
| KnownInstanceType::Optional
97+
| KnownInstanceType::RedKnotUnknown => None,
9798
KnownInstanceType::Any => Some(Self::Any),
9899
// TODO: Classes inheriting from `typing.Type` et al. also have `Generic` in their MRO
99100
KnownInstanceType::Dict => {

crates/red_knot_python_semantic/src/types/infer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5217,7 +5217,9 @@ impl<'db> TypeInferenceBuilder<'db> {
52175217
);
52185218
Type::Unknown
52195219
}
5220-
KnownInstanceType::TypingSelf | KnownInstanceType::TypeAlias => {
5220+
KnownInstanceType::TypingSelf
5221+
| KnownInstanceType::TypeAlias
5222+
| KnownInstanceType::RedKnotUnknown => {
52215223
self.context.report_lint(
52225224
&INVALID_TYPE_FORM,
52235225
subscript.into(),

crates/red_knot_vendored/vendor/typeshed/stdlib/red_knot.pyi

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1-
from typing import TypeVarTuple
1+
from typing import TypeVarTuple, _SpecialForm
22

3+
4+
# Special operations
5+
class TypeOf[T]: ...
6+
7+
def assert_true(x: object) -> None: ...
8+
9+
10+
# Type extensions
311
Ts = TypeVarTuple("Ts")
412

5-
# Operations on types
613
class Not[T]: ...
714
class Intersection[*Ts]: ...
8-
class ClassLiteral[T]: ...
15+
16+
Unknown = object()
917

1018
# Predicates on types
1119
class _IsEquivalentTo[S, T]: ...
@@ -23,9 +31,3 @@ def is_disjoint_from[S, T](x: S, y: T) -> _IsDisjointFrom[S, T]: ...
2331
def is_fully_static[T](x: T) -> _IsFullyStatic[T]: ...
2432
def is_singleton[T](x: T) -> _IsSingleton[T]: ...
2533
def is_single_valued[T](x: T) -> _IsSingleValued[T]: ...
26-
27-
28-
# Special operations
29-
class TypeOf[T]: ...
30-
31-
def assert_true(x: object) -> None: ...

0 commit comments

Comments
 (0)