[ty] Simplify unions containing multiple type variables during inference (#21275)

## Summary

Splitting this one out from https://github.com/astral-sh/ruff/pull/21210. This is also something that should be made obselete by the new constraint solver, but is easy enough to fix now.
This commit is contained in:
Ibraheem Ahmed 2025-11-05 10:03:19 -05:00 committed by GitHub
parent 7569b09bdd
commit 5c69e00d1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 2 deletions

View file

@ -474,6 +474,16 @@ def g(x: str):
f(prefix=x, suffix=".tar.gz")
```
If the type variable is present multiple times in the union, we choose the correct union element to
infer against based on the argument type:
```py
def h[T](x: list[T] | dict[T, T]) -> T | None: ...
def _(x: list[int], y: dict[int, int]):
reveal_type(h(x)) # revealed: int | None
reveal_type(h(y)) # revealed: int | None
```
## Nested functions see typevars bound in outer function
```py

View file

@ -1397,11 +1397,13 @@ impl<'db> SpecializationBuilder<'db> {
return Ok(());
}
// Remove the union elements that are not related to `formal`.
// Remove the union elements from `actual` that are not related to `formal`, and vice
// versa.
//
// For example, if `formal` is `list[T]` and `actual` is `list[int] | None`, we want to specialize `T`
// to `int`.
// to `int`, and so ignore the `None`.
let actual = actual.filter_disjoint_elements(self.db, formal, self.inferable);
let formal = formal.filter_disjoint_elements(self.db, actual, self.inferable);
match (formal, actual) {
// TODO: We haven't implemented a full unification solver yet. If typevars appear in