Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _(
reveal_type(k) # revealed: Unknown
reveal_type(p) # revealed: Unknown
reveal_type(q) # revealed: int | Unknown
reveal_type(r) # revealed: @Todo(generics)
reveal_type(r) # revealed: @Todo(unknown type subscript)
```

## Invalid Collection based AST nodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ from other import Literal
a1: Literal[26]

def f():
reveal_type(a1) # revealed: @Todo(generics)
reveal_type(a1) # revealed: @Todo(unknown type subscript)
```

## Detecting typing_extensions.Literal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ reveal_type(d) # revealed: tuple[tuple[str, str], tuple[int, int]]
reveal_type(e) # revealed: @Todo(full tuple[...] support)
reveal_type(f) # revealed: @Todo(full tuple[...] support)
reveal_type(g) # revealed: @Todo(full tuple[...] support)
reveal_type(h) # revealed: tuple[@Todo(generics), @Todo(generics)]
reveal_type(h) # revealed: tuple[@Todo(specialized non-generic class), @Todo(specialized non-generic class)]

reveal_type(i) # revealed: tuple[str | int, str | int]
reveal_type(j) # revealed: tuple[str | int]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,7 @@ functions are instances of that class:
def f(): ...

reveal_type(f.__defaults__) # revealed: @Todo(full tuple[...] support) | None
reveal_type(f.__kwdefaults__) # revealed: @Todo(generics) | None
reveal_type(f.__kwdefaults__) # revealed: @Todo(specialized non-generic class) | None
```

Some attributes are special-cased, however:
Expand Down Expand Up @@ -1716,7 +1716,8 @@ reveal_type(False.real) # revealed: Literal[0]
All attribute access on literal `bytes` types is currently delegated to `builtins.bytes`:

```py
reveal_type(b"foo".join) # revealed: bound method Literal[b"foo"].join(iterable_of_bytes: @Todo(generics), /) -> bytes
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: @Todo(specialized non-generic class), /) -> bytes
reveal_type(b"foo".join)
# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`), start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
reveal_type(b"foo".endswith)
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function object. We model this explicitly, which means that we can access `__kwd
methods, even though it is not available on `types.MethodType`:

```py
reveal_type(bound_method.__kwdefaults__) # revealed: @Todo(generics) | None
reveal_type(bound_method.__kwdefaults__) # revealed: @Todo(specialized non-generic class) | None
```

## Basic method calls on class objects and instances
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ def f(x: int) -> int:
return x**2

# TODO: Should be `_lru_cache_wrapper[int]`
reveal_type(f) # revealed: @Todo(generics)
reveal_type(f) # revealed: @Todo(specialized non-generic class)

# TODO: Should be `int`
reveal_type(f(1)) # revealed: @Todo(generics)
reveal_type(f(1)) # revealed: @Todo(specialized non-generic class)
```

## Lambdas as decorators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ from knot_extensions import Unknown

def f(x: Any, y: Unknown, z: Any | str | int):
a = cast(dict[str, Any], x)
reveal_type(a) # revealed: @Todo(generics)
reveal_type(a) # revealed: @Todo(specialized non-generic class)

b = cast(Any, y)
reveal_type(b) # revealed: Any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,12 @@ consistent with each other.

```py
class C[T]:
def __new__(cls, x: T) -> "C"[T]:
def __new__(cls, x: T) -> "C[T]":
return object.__new__(cls)

reveal_type(C(1)) # revealed: C[Literal[1]]

# TODO: error: [invalid-argument-type]
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
wrong_innards: C[int] = C("five")
```

Expand All @@ -181,48 +181,48 @@ class C[T]:

reveal_type(C(1)) # revealed: C[Literal[1]]

# TODO: error: [invalid-argument-type]
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
wrong_innards: C[int] = C("five")
```

## Identical `__new__` and `__init__` signatures

```py
class C[T]:
def __new__(cls, x: T) -> "C"[T]:
def __new__(cls, x: T) -> "C[T]":
return object.__new__(cls)

def __init__(self, x: T) -> None: ...

reveal_type(C(1)) # revealed: C[Literal[1]]

# TODO: error: [invalid-argument-type]
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
wrong_innards: C[int] = C("five")
```

## Compatible `__new__` and `__init__` signatures

```py
class C[T]:
def __new__(cls, *args, **kwargs) -> "C"[T]:
def __new__(cls, *args, **kwargs) -> "C[T]":
return object.__new__(cls)

def __init__(self, x: T) -> None: ...

reveal_type(C(1)) # revealed: C[Literal[1]]

# TODO: error: [invalid-argument-type]
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
wrong_innards: C[int] = C("five")

class D[T]:
def __new__(cls, x: T) -> "D"[T]:
def __new__(cls, x: T) -> "D[T]":
return object.__new__(cls)

def __init__(self, *args, **kwargs) -> None: ...

reveal_type(D(1)) # revealed: D[Literal[1]]

# TODO: error: [invalid-argument-type]
# error: [invalid-assignment] "Object of type `D[Literal["five"]]` is not assignable to `D[int]`"
wrong_innards: D[int] = D("five")
```

Expand All @@ -247,7 +247,7 @@ reveal_type(C(1, "string")) # revealed: C[Unknown]
# error: [invalid-argument-type]
reveal_type(C(1, True)) # revealed: C[Unknown]

# TODO: error for the correct reason
# TODO: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `S`, found `Literal[1]`"
wrong_innards: C[int] = C("five", 1)
```
Expand Down
40 changes: 20 additions & 20 deletions crates/red_knot_python_semantic/resources/mdtest/generics/pep695.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,19 @@ is.)
from knot_extensions import is_fully_static, static_assert
from typing import Any

def unbounded_unconstrained[T](t: list[T]) -> None:
def unbounded_unconstrained[T](t: T) -> None:
static_assert(is_fully_static(T))

def bounded[T: int](t: list[T]) -> None:
def bounded[T: int](t: T) -> None:
static_assert(is_fully_static(T))

def bounded_by_gradual[T: Any](t: list[T]) -> None:
def bounded_by_gradual[T: Any](t: T) -> None:
static_assert(not is_fully_static(T))

def constrained[T: (int, str)](t: list[T]) -> None:
def constrained[T: (int, str)](t: T) -> None:
static_assert(is_fully_static(T))

def constrained_by_gradual[T: (int, Any)](t: list[T]) -> None:
def constrained_by_gradual[T: (int, Any)](t: T) -> None:
static_assert(not is_fully_static(T))
```

Expand All @@ -99,7 +99,7 @@ class Base(Super): ...
class Sub(Base): ...
class Unrelated: ...

def unbounded_unconstrained[T, U](t: list[T], u: list[U]) -> None:
def unbounded_unconstrained[T, U](t: T, u: U) -> None:
static_assert(is_assignable_to(T, T))
static_assert(is_assignable_to(T, object))
static_assert(not is_assignable_to(T, Super))
Expand Down Expand Up @@ -129,7 +129,7 @@ is a final class, since the typevar can still be specialized to `Never`.)
from typing import Any
from typing_extensions import final

def bounded[T: Super](t: list[T]) -> None:
def bounded[T: Super](t: T) -> None:
static_assert(is_assignable_to(T, Super))
static_assert(not is_assignable_to(T, Sub))
static_assert(not is_assignable_to(Super, T))
Expand All @@ -140,7 +140,7 @@ def bounded[T: Super](t: list[T]) -> None:
static_assert(not is_subtype_of(Super, T))
static_assert(not is_subtype_of(Sub, T))

def bounded_by_gradual[T: Any](t: list[T]) -> None:
def bounded_by_gradual[T: Any](t: T) -> None:
static_assert(is_assignable_to(T, Any))
static_assert(is_assignable_to(Any, T))
static_assert(is_assignable_to(T, Super))
Expand All @@ -158,7 +158,7 @@ def bounded_by_gradual[T: Any](t: list[T]) -> None:
@final
class FinalClass: ...

def bounded_final[T: FinalClass](t: list[T]) -> None:
def bounded_final[T: FinalClass](t: T) -> None:
static_assert(is_assignable_to(T, FinalClass))
static_assert(not is_assignable_to(FinalClass, T))

Expand All @@ -172,14 +172,14 @@ true even if both typevars are bounded by the same final class, since you can sp
typevars to `Never` in addition to that final class.

```py
def two_bounded[T: Super, U: Super](t: list[T], u: list[U]) -> None:
def two_bounded[T: Super, U: Super](t: T, u: U) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))

static_assert(not is_subtype_of(T, U))
static_assert(not is_subtype_of(U, T))

def two_final_bounded[T: FinalClass, U: FinalClass](t: list[T], u: list[U]) -> None:
def two_final_bounded[T: FinalClass, U: FinalClass](t: T, u: U) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))

Expand All @@ -194,7 +194,7 @@ intersection of all of its constraints is a subtype of the typevar.
```py
from knot_extensions import Intersection

def constrained[T: (Base, Unrelated)](t: list[T]) -> None:
def constrained[T: (Base, Unrelated)](t: T) -> None:
static_assert(not is_assignable_to(T, Super))
static_assert(not is_assignable_to(T, Base))
static_assert(not is_assignable_to(T, Sub))
Expand All @@ -219,7 +219,7 @@ def constrained[T: (Base, Unrelated)](t: list[T]) -> None:
static_assert(not is_subtype_of(Super | Unrelated, T))
static_assert(is_subtype_of(Intersection[Base, Unrelated], T))

def constrained_by_gradual[T: (Base, Any)](t: list[T]) -> None:
def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
static_assert(is_assignable_to(T, Super))
static_assert(is_assignable_to(T, Base))
static_assert(not is_assignable_to(T, Sub))
Expand Down Expand Up @@ -261,7 +261,7 @@ distinct constraints, meaning that there is (still) no guarantee that they will
the same type.

```py
def two_constrained[T: (int, str), U: (int, str)](t: list[T], u: list[U]) -> None:
def two_constrained[T: (int, str), U: (int, str)](t: T, u: U) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))

Expand All @@ -271,7 +271,7 @@ def two_constrained[T: (int, str), U: (int, str)](t: list[T], u: list[U]) -> Non
@final
class AnotherFinalClass: ...

def two_final_constrained[T: (FinalClass, AnotherFinalClass), U: (FinalClass, AnotherFinalClass)](t: list[T], u: list[U]) -> None:
def two_final_constrained[T: (FinalClass, AnotherFinalClass), U: (FinalClass, AnotherFinalClass)](t: T, u: U) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))

Expand All @@ -290,7 +290,7 @@ non-singleton type.
```py
from knot_extensions import is_singleton, is_single_valued, static_assert

def unbounded_unconstrained[T](t: list[T]) -> None:
def unbounded_unconstrained[T](t: T) -> None:
static_assert(not is_singleton(T))
static_assert(not is_single_valued(T))
```
Expand All @@ -299,7 +299,7 @@ A bounded typevar is not a singleton, even if its bound is a singleton, since it
specialized to `Never`.

```py
def bounded[T: None](t: list[T]) -> None:
def bounded[T: None](t: T) -> None:
static_assert(not is_singleton(T))
static_assert(not is_single_valued(T))
```
Expand All @@ -310,14 +310,14 @@ specialize a constrained typevar to a subtype of a constraint.)
```py
from typing_extensions import Literal

def constrained_non_singletons[T: (int, str)](t: list[T]) -> None:
def constrained_non_singletons[T: (int, str)](t: T) -> None:
static_assert(not is_singleton(T))
static_assert(not is_single_valued(T))

def constrained_singletons[T: (Literal[True], Literal[False])](t: list[T]) -> None:
def constrained_singletons[T: (Literal[True], Literal[False])](t: T) -> None:
static_assert(is_singleton(T))

def constrained_single_valued[T: (Literal[True], tuple[()])](t: list[T]) -> None:
def constrained_single_valued[T: (Literal[True], tuple[()])](t: T) -> None:
static_assert(is_single_valued(T))
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,13 @@ S = TypeVar("S")

def f(x: T) -> None:
x: list[T] = []
# TODO: error
# TODO: invalid-assignment error
y: list[S] = []

# TODO: no error
# error: [invalid-base]
class C(Generic[T]):
# TODO: error
# TODO: error: cannot use S if it's not in the current generic context
x: list[S] = []

# This is not an error, as shown in the previous test
Expand All @@ -200,11 +200,11 @@ S = TypeVar("S")

def f[T](x: T) -> None:
x: list[T] = []
# TODO: error
# TODO: invalid assignment error
y: list[S] = []

class C[T]:
# TODO: error
# TODO: error: cannot use S if it's not in the current generic context
x: list[S] = []

def m1(self, x: S) -> S:
Expand Down Expand Up @@ -288,7 +288,7 @@ class C[T]:
ok1: list[T] = []

class Bad:
# TODO: error
# TODO: error: cannot refer to T in nested scope
bad: list[T] = []

class Inner[S]: ...
Expand Down
Loading
Loading