ruff
d3791168 - [ty] Correctly instantiate generic class that inherits `__init__` from generic base class (#19693)

Commit
63 days ago
[ty] Correctly instantiate generic class that inherits `__init__` from generic base class (#19693) This is subtle, and the root cause became more apparent with #19604, since we now have many more cases of superclasses and subclasses using different typevars. The issue is easiest to see in the following: ```py class C[T]: def __init__(self, t: T) -> None: ... class D[U](C[T]): pass reveal_type(C(1)) # revealed: C[int] reveal_type(D(1)) # should be: D[int] ``` When instantiating a generic class, the `__init__` method inherits the generic context of that class. This lets our call binding machinery infer a specialization for that context. Prior to this PR, the instantiation of `C` worked just fine. Its `__init__` method would inherit the `[T]` generic context, and we would infer `{T = int}` as the specialization based on the argument parameters. It didn't work for `D`. The issue is that the `__init__` method was inheriting the generic context of the class where `__init__` was defined (here, `C` and `[T]`). At the call site, we would then infer `{T = int}` as the specialization — but that wouldn't help us specialize `D[U]`, since `D` does not have `T` in its generic context! Instead, the `__init__` method should inherit the generic context of the class that we are performing the lookup on (here, `D` and `[U]`). That lets us correctly infer `{U = int}` as the specialization, which we can successfully apply to `D[U]`. (Note that `__init__` refers to `C`'s typevars in its signature, but that's okay; our member lookup logic already applies the `T = U` specialization when returning a member of `C` while performing a lookup on `D`, transforming its signature from `(Self, T) -> None` to `(Self, U) -> None`.) Closes https://github.com/astral-sh/ty/issues/588
Author
Parents
Loading