mirror of https://github.com/astral-sh/ruff
[ty] Prefer exact matches when solving constrained type variables (#21165)
## Summary The solver is currently order-dependent, and will choose a supertype over the exact type if it appears earlier in the list of constraints. We could be smarter and try to choose the most precise subtype, but I imagine this is something the new constraint solver will fix anyways, and this fixes the issue showing up on https://github.com/astral-sh/ruff/pull/21070.
This commit is contained in:
parent
cf4e82d4b0
commit
1d6ae8596a
|
|
@ -545,3 +545,28 @@ def f(x: T, y: Not[T]) -> T:
|
||||||
y = x # error: [invalid-assignment]
|
y = x # error: [invalid-assignment]
|
||||||
return x
|
return x
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Prefer exact matches for constrained typevars
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
class Base: ...
|
||||||
|
class Sub(Base): ...
|
||||||
|
|
||||||
|
# We solve to `Sub`, regardless of the order of constraints.
|
||||||
|
T = TypeVar("T", Base, Sub)
|
||||||
|
T2 = TypeVar("T2", Sub, Base)
|
||||||
|
|
||||||
|
def f(x: T) -> list[T]:
|
||||||
|
return [x]
|
||||||
|
|
||||||
|
def f2(x: T2) -> list[T2]:
|
||||||
|
return [x]
|
||||||
|
|
||||||
|
x: list[Sub] = f(Sub())
|
||||||
|
reveal_type(x) # revealed: list[Sub]
|
||||||
|
|
||||||
|
y: list[Sub] = f2(Sub())
|
||||||
|
reveal_type(y) # revealed: list[Sub]
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1483,6 +1483,14 @@ impl<'db> SpecializationBuilder<'db> {
|
||||||
self.add_type_mapping(bound_typevar, ty);
|
self.add_type_mapping(bound_typevar, ty);
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
|
// Prefer an exact match first.
|
||||||
|
for constraint in constraints.elements(self.db) {
|
||||||
|
if ty == *constraint {
|
||||||
|
self.add_type_mapping(bound_typevar, ty);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for constraint in constraints.elements(self.db) {
|
for constraint in constraints.elements(self.db) {
|
||||||
if ty
|
if ty
|
||||||
.when_assignable_to(self.db, *constraint, self.inferable)
|
.when_assignable_to(self.db, *constraint, self.inferable)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue