mirror of https://github.com/astral-sh/ruff
[ty] Do not promote literals in contravariant positions of generic specializations (#21171)
## Summary closes https://github.com/astral-sh/ty/issues/1284 supersedes https://github.com/astral-sh/ruff/pull/20950 by @ibraheemdev ## Test Plan New regression test
This commit is contained in:
parent
ff3a6a8fbd
commit
1734ddfb3e
|
|
@ -1,5 +1,10 @@
|
|||
# Literal promotion
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
There are certain places where we promote literals to their common supertype:
|
||||
|
||||
```py
|
||||
|
|
@ -30,3 +35,34 @@ def double_negation(callback: Callable[[Callable[[Literal[1]], None]], None]):
|
|||
|
||||
reveal_type([callback]) # revealed: list[Unknown | (((int, /) -> None, /) -> None)]
|
||||
```
|
||||
|
||||
Literal promotion should also not apply recursively to type arguments in contravariant/invariant
|
||||
position:
|
||||
|
||||
```py
|
||||
class Bivariant[T]:
|
||||
pass
|
||||
|
||||
class Covariant[T]:
|
||||
def pop(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
class Contravariant[T]:
|
||||
def push(self, value: T) -> None:
|
||||
pass
|
||||
|
||||
class Invariant[T]:
|
||||
x: T
|
||||
|
||||
def _(
|
||||
bivariant: Bivariant[Literal[1]],
|
||||
covariant: Covariant[Literal[1]],
|
||||
contravariant: Contravariant[Literal[1]],
|
||||
invariant: Invariant[Literal[1]],
|
||||
):
|
||||
reveal_type([bivariant]) # revealed: list[Unknown | Bivariant[int]]
|
||||
reveal_type([covariant]) # revealed: list[Unknown | Covariant[int]]
|
||||
|
||||
reveal_type([contravariant]) # revealed: list[Unknown | Contravariant[Literal[1]]]
|
||||
reveal_type([invariant]) # revealed: list[Unknown | Invariant[Literal[1]]]
|
||||
```
|
||||
|
|
|
|||
|
|
@ -969,10 +969,15 @@ impl<'db> Specialization<'db> {
|
|||
let types: Box<[_]> = self
|
||||
.types(db)
|
||||
.iter()
|
||||
.zip(self.generic_context(db).variables(db))
|
||||
.enumerate()
|
||||
.map(|(i, ty)| {
|
||||
.map(|(i, (ty, typevar))| {
|
||||
let tcx = TypeContext::new(tcx.get(i).copied());
|
||||
ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
|
||||
if typevar.variance(db).is_covariant() {
|
||||
ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
|
||||
} else {
|
||||
ty.apply_type_mapping_impl(db, &type_mapping.flip(), tcx, visitor)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ impl TypeVarVariance {
|
|||
TypeVarVariance::Bivariant => TypeVarVariance::Bivariant,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn is_covariant(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
TypeVarVariance::Covariant | TypeVarVariance::Bivariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FromIterator<Self> for TypeVarVariance {
|
||||
|
|
|
|||
Loading…
Reference in New Issue