Skip to content

Commit

Permalink
Add red_knot.Unknown
Browse files Browse the repository at this point in the history
  • Loading branch information
sharkdp committed Jan 2, 2025
1 parent f286bc5 commit 9795b39
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 16 deletions.
21 changes: 20 additions & 1 deletion crates/red_knot_python_semantic/resources/mdtest/type_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This document describes the internal `red_knot` API for manipulating types and querying their
properties.

## Type operations
## Type extensions

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

### Unknown type

The `Unknown` type is a special type that we use to represent actually unknown types (no
annotation), as opposed to `Any` which represents an explicitly unknown type.

```py
from red_knot import Unknown

assert_true(is_assignable_to(Unknown, int))
assert_true(is_assignable_to(int, Unknown))

assert_true(not is_fully_static(Unknown))

x: Unknown = 1

def _() -> None:
reveal_type(x) # revealed: Unknown
```

## Type predicates

The `red_knot` module also provides predicates to test various properties of types. These are
Expand Down
19 changes: 15 additions & 4 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2085,10 +2085,14 @@ impl<'db> Type<'db> {
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::BareLiteral],
fallback_type: Type::Unknown,
}),
Type::KnownInstance(KnownInstanceType::RedKnotUnknown) => Ok(Type::Unknown),
Type::Todo(_) => Ok(*self),
_ => Ok(todo_type!(
"Unsupported or invalid type in a type expression"
)),
_ => {
dbg!(self);
Ok(todo_type!(
"Unsupported or invalid type in a type expression"
))
}
}
}

Expand Down Expand Up @@ -2595,6 +2599,7 @@ pub enum KnownInstanceType<'db> {
TypeIs,
ReadOnly,
// TODO: fill this enum out with more special forms, etc.
RedKnotUnknown,
}

impl<'db> KnownInstanceType<'db> {
Expand Down Expand Up @@ -2633,6 +2638,7 @@ impl<'db> KnownInstanceType<'db> {
Self::ChainMap => "ChainMap",
Self::OrderedDict => "OrderedDict",
Self::ReadOnly => "ReadOnly",
Self::RedKnotUnknown => "Unknown",
}
}

Expand Down Expand Up @@ -2671,7 +2677,8 @@ impl<'db> KnownInstanceType<'db> {
| Self::ChainMap
| Self::OrderedDict
| Self::ReadOnly
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
| Self::TypeAliasType(_)
| Self::RedKnotUnknown => Truthiness::AlwaysTrue,
}
}

Expand Down Expand Up @@ -2711,6 +2718,7 @@ impl<'db> KnownInstanceType<'db> {
Self::ReadOnly => "typing.ReadOnly",
Self::TypeVar(typevar) => typevar.name(db),
Self::TypeAliasType(_) => "typing.TypeAliasType",
Self::RedKnotUnknown => "red_knot.Unknown",
}
}

Expand Down Expand Up @@ -2750,6 +2758,7 @@ impl<'db> KnownInstanceType<'db> {
Self::OrderedDict => KnownClass::StdlibAlias,
Self::TypeVar(_) => KnownClass::TypeVar,
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
Self::RedKnotUnknown => KnownClass::Object,
}
}

Expand Down Expand Up @@ -2795,6 +2804,7 @@ impl<'db> KnownInstanceType<'db> {
"Concatenate" => Self::Concatenate,
"NotRequired" => Self::NotRequired,
"LiteralString" => Self::LiteralString,
"Unknown" => Self::RedKnotUnknown,
_ => return None,
};

Expand Down Expand Up @@ -2844,6 +2854,7 @@ impl<'db> KnownInstanceType<'db> {
| Self::TypeVar(_) => {
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
}
Self::RedKnotUnknown => matches!(module, KnownModule::RedKnot),
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/red_knot_python_semantic/src/types/class_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ impl<'db> ClassBase<'db> {
| KnownInstanceType::Required
| KnownInstanceType::TypeAlias
| KnownInstanceType::ReadOnly
| KnownInstanceType::Optional => None,
| KnownInstanceType::Optional
| KnownInstanceType::RedKnotUnknown => None,
KnownInstanceType::Any => Some(Self::Any),
// TODO: Classes inheriting from `typing.Type` et al. also have `Generic` in their MRO
KnownInstanceType::Dict => {
Expand Down
4 changes: 3 additions & 1 deletion crates/red_knot_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5217,7 +5217,9 @@ impl<'db> TypeInferenceBuilder<'db> {
);
Type::Unknown
}
KnownInstanceType::TypingSelf | KnownInstanceType::TypeAlias => {
KnownInstanceType::TypingSelf
| KnownInstanceType::TypeAlias
| KnownInstanceType::RedKnotUnknown => {
self.context.report_lint(
&INVALID_TYPE_FORM,
subscript.into(),
Expand Down
20 changes: 11 additions & 9 deletions crates/red_knot_vendored/vendor/typeshed/stdlib/red_knot.pyi
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
from typing import TypeVarTuple
from typing import TypeVarTuple, _SpecialForm


# Special operations
class TypeOf[T]: ...

def assert_true(x: object) -> None: ...


# Type extensions
Ts = TypeVarTuple("Ts")

# Operations on types
class Not[T]: ...
class Intersection[*Ts]: ...
class ClassLiteral[T]: ...

Unknown = object()

# Predicates on types
class _IsEquivalentTo[S, T]: ...
Expand All @@ -23,9 +31,3 @@ def is_disjoint_from[S, T](x: S, y: T) -> _IsDisjointFrom[S, T]: ...
def is_fully_static[T](x: T) -> _IsFullyStatic[T]: ...
def is_singleton[T](x: T) -> _IsSingleton[T]: ...
def is_single_valued[T](x: T) -> _IsSingleValued[T]: ...


# Special operations
class TypeOf[T]: ...

def assert_true(x: object) -> None: ...

0 comments on commit 9795b39

Please sign in to comment.