Skip to content

Commit

Permalink
Skip lookup on protocols
Browse files Browse the repository at this point in the history
  • Loading branch information
sharkdp committed Mar 3, 2025
1 parent 4d5accf commit 76aa5c3
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import typing
class ListSubclass(typing.List): ...

# TODO: should have `Generic`, should not have `Unknown`
# revealed: tuple[Literal[ListSubclass], Literal[list], Literal[object]]
# revealed: tuple[Literal[ListSubclass], Literal[list], Literal[MutableSequence], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
reveal_type(ListSubclass.__mro__)

class DictSubclass(typing.Dict): ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class MySecureBox[T](MyBox[T]): ...
secure_box: MySecureBox[int] = MySecureBox(5)
reveal_type(secure_box) # revealed: MySecureBox
# TODO reveal int
reveal_type(secure_box.data) # revealed: Unknown
reveal_type(secure_box.data) # revealed: T
```

## Cyclical class definition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ is unbound.
```py
reveal_type(__name__) # revealed: str
reveal_type(__file__) # revealed: str | None
reveal_type(__loader__) # revealed: @Todo(Support for protocols) | None
reveal_type(__loader__) # revealed: LoaderProtocol | None
reveal_type(__package__) # revealed: str | None
reveal_type(__doc__) # revealed: str | None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class Foo[T]: ...
class Bar(Foo[Bar]): ...

reveal_type(Bar) # revealed: Literal[Bar]
reveal_type(Bar.__mro__) # revealed: tuple[Literal[Bar], Unknown, Literal[object]]

# TODO: Support for generic `Foo`
reveal_type(Bar.__mro__) # revealed: tuple[Literal[Bar], Literal[Foo], Literal[object]]
```

## Access to attributes declarated in stubs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,7 @@ class C(Tuple): ...

# Runtime value: `(C, tuple, typing.Generic, object)`
# TODO: Add `Generic` to the MRO
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[tuple], Literal[object]]
reveal_type(
C.__mro__
) # revealed: tuple[Literal[C], Literal[tuple], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
```
3 changes: 3 additions & 0 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3181,6 +3181,8 @@ pub enum DynamicType {
///
/// This variant should be created with the `todo_type!` macro.
Todo(TodoType),
/// Temporary type until we support protocols.
TodoProtocol,
}

impl std::fmt::Display for DynamicType {
Expand All @@ -3191,6 +3193,7 @@ impl std::fmt::Display for DynamicType {
// `DynamicType::Todo`'s display should be explicit that is not a valid display of
// any other type
DynamicType::Todo(todo) => write!(f, "@Todo{todo}"),
DynamicType::TodoProtocol => f.write_str("@Todo(protocol)"),
}
}
}
Expand Down
14 changes: 12 additions & 2 deletions crates/red_knot_python_semantic/src/types/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::{
Boundness, LookupError, LookupResult, Symbol, SymbolAndQualifiers,
},
types::{
definition_expression_type, CallArguments, CallError, MetaclassCandidate, TupleType,
UnionBuilder, UnionCallError,
definition_expression_type, CallArguments, CallError, DynamicType, MetaclassCandidate,
TupleType, UnionBuilder, UnionCallError,
},
Db, KnownModule, Program,
};
Expand Down Expand Up @@ -339,6 +339,11 @@ impl<'db> Class<'db> {

for superclass in self.iter_mro(db) {
match superclass {
ClassBase::Dynamic(DynamicType::TodoProtocol) => {
// TODO: We currently skip protocols when looking up class members, in order to
// avoid creating many dynamic types in our test suite that would otherwise
// result from looking up attributes on builtin types like `str`, `list`, `tuple`
}
ClassBase::Dynamic(_) => {
// Note: calling `Type::from(superclass).member()` would be incorrect here.
// What we'd really want is a `Type::Any.own_class_member()` method,
Expand Down Expand Up @@ -407,6 +412,11 @@ impl<'db> Class<'db> {

for superclass in self.iter_mro(db) {
match superclass {
ClassBase::Dynamic(DynamicType::TodoProtocol) => {
// TODO: We currently skip protocols when looking up instance members, in order to
// avoid creating many dynamic types in our test suite that would otherwise
// result from looking up attributes on builtin types like `str`, `list`, `tuple`
}
ClassBase::Dynamic(_) => {
return SymbolAndQualifiers::todo(
"instance attribute on class with dynamic base",
Expand Down
4 changes: 1 addition & 3 deletions crates/red_knot_python_semantic/src/types/class_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ impl<'db> ClassBase<'db> {
KnownInstanceType::Callable => {
Self::try_from_type(db, todo_type!("Support for Callable as a base class"))
}
KnownInstanceType::Protocol => {
Self::try_from_type(db, todo_type!("Support for protocols"))
}
KnownInstanceType::Protocol => Some(ClassBase::Dynamic(DynamicType::TodoProtocol)),
},
}
}
Expand Down
15 changes: 13 additions & 2 deletions crates/red_knot_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4005,6 +4005,8 @@ impl<'db> TypeInferenceBuilder<'db> {
| (_, unknown @ Type::Dynamic(DynamicType::Unknown), _) => Some(unknown),
(todo @ Type::Dynamic(DynamicType::Todo(_)), _, _)
| (_, todo @ Type::Dynamic(DynamicType::Todo(_)), _) => Some(todo),
(todo @ Type::Dynamic(DynamicType::TodoProtocol), _, _)
| (_, todo @ Type::Dynamic(DynamicType::TodoProtocol), _) => Some(todo),
(Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never),

(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some(
Expand Down Expand Up @@ -5178,6 +5180,9 @@ impl<'db> TypeInferenceBuilder<'db> {
value_ty,
Type::IntLiteral(i64::from(bool)),
),
(Type::KnownInstance(KnownInstanceType::Protocol), _) => {
Type::Dynamic(DynamicType::TodoProtocol)
}
(value_ty, slice_ty) => {
// If the class defines `__getitem__`, return its return type.
//
Expand Down Expand Up @@ -5283,7 +5288,13 @@ impl<'db> TypeInferenceBuilder<'db> {
);
}

Type::unknown()
match value_ty {
Type::ClassLiteral(_) => {
// TODO: proper support for generic classes
value_ty
}
_ => Type::unknown(),
}
}
}
}
Expand Down Expand Up @@ -6146,7 +6157,7 @@ impl<'db> TypeInferenceBuilder<'db> {
}
KnownInstanceType::Protocol => {
self.infer_type_expression(arguments_slice);
todo_type!("Generic protocol")
Type::Dynamic(DynamicType::TodoProtocol)
}
KnownInstanceType::NoReturn
| KnownInstanceType::Never
Expand Down
3 changes: 3 additions & 0 deletions crates/red_knot_python_semantic/src/types/type_ordering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,8 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering

#[cfg(not(debug_assertions))]
(DynamicType::Todo(TodoType), DynamicType::Todo(TodoType)) => Ordering::Equal,

(DynamicType::TodoProtocol, _) => Ordering::Less,
(_, DynamicType::TodoProtocol) => Ordering::Greater,
}
}

0 comments on commit 76aa5c3

Please sign in to comment.