ruff
33b942c7 - [ty] Handle annotated `self` parameter in constructor of non-invariant generic classes (#21325)

Commit
167 days ago
[ty] Handle annotated `self` parameter in constructor of non-invariant generic classes (#21325) This manifested as an error when inferring the type of a PEP-695 generic class via its constructor parameters: ```py class D[T, U]: @overload def __init__(self: "D[str, U]", u: U) -> None: ... @overload def __init__(self, t: T, u: U) -> None: ... def __init__(self, *args) -> None: ... # revealed: D[Unknown, str] # SHOULD BE: D[str, str] reveal_type(D("string")) ``` This manifested because `D` is inferred to be bivariant in both `T` and `U`. We weren't seeing this in the equivalent example for legacy typevars, since those default to invariant. (This issue also showed up for _covariant_ typevars, so this issue was not limited to bivariance.) The underlying cause was because of a heuristic that we have in our current constraint solver, which attempts to handle situations like this: ```py def f[T](t: T | None): ... f(None) ``` Here, the `None` argument matches the non-typevar union element, so this argument should not add any constraints on what `T` can specialize to. Our previous heuristic would check for this by seeing if the argument type is a subtype of the parameter annotation as a whole — even if it isn't a union! That would cause us to erroneously ignore the `self` parameter in our constructor call, since bivariant classes are equivalent to each other, regardless of their specializations. The quick fix is to move this heuristic "down a level", so that we only apply it when the parameter annotation is a union. This heuristic should go away completely :crossed_fingers: with the new constraint solver.
Author
Parents
Loading