mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-07 00:50:37 +00:00
[ty] Introduce TypeRelation::Redundancy
(#20602)
## Summary The union `T | U` can be validly simplified to `U` iff: 1. `T` is a subtype of `U` OR 2. `T` is equivalent to `U` OR 3. `U` is a union and contains a type that is equivalent to `T` OR 4. `T` is an intersection and contains a type that is equivalent to `U` (In practice, the only situation in which 2, 3 or 4 would be true when (1) was not true would be if `T` or `U` is a dynamic type.) Currently we achieve these simplifications in the union builder by doing something along the lines of `t.is_subtype_of(db, u) || t.is_equivalent_to_(db, u) || t.into_intersection().is_some_and(|intersection| intersection.positive(db).contains(&u)) || u.into_union().is_some_and(|union| union.elements(db).contains(&t))`. But this is both slow and misses some cases (it doesn't simplify the union `Any | (Unknown & ~None)` to `Any`, for example). We can improve the consistency and performance of our union simplifications by adding a third type relation that sits in between `TypeRelation::Subtyping` and `TypeRelation::Assignability`: `TypeRelation::UnionSimplification`. This change leads to simpler, more user-friendly types due to the more consistent simplification. It also lead to a pretty huge performance improvement! ## Test Plan Existing tests, plus some new ones.
This commit is contained in:
parent
673167a565
commit
c91b457044
9 changed files with 281 additions and 92 deletions
|
@ -138,6 +138,11 @@ static_assert(is_equivalent_to(Any, Any | Intersection[Any, str]))
|
|||
static_assert(is_equivalent_to(Any, Intersection[str, Any] | Any))
|
||||
static_assert(is_equivalent_to(Any, Any | Intersection[Any, Not[None]]))
|
||||
static_assert(is_equivalent_to(Any, Intersection[Not[None], Any] | Any))
|
||||
|
||||
static_assert(is_equivalent_to(Any, Unknown | Intersection[Unknown, str]))
|
||||
static_assert(is_equivalent_to(Any, Intersection[str, Unknown] | Unknown))
|
||||
static_assert(is_equivalent_to(Any, Unknown | Intersection[Unknown, Not[None]]))
|
||||
static_assert(is_equivalent_to(Any, Intersection[Not[None], Unknown] | Unknown))
|
||||
```
|
||||
|
||||
## Tuples
|
||||
|
|
|
@ -306,3 +306,74 @@ def _(c: BC, d: BD):
|
|||
reveal_type(c) # revealed: Literal[b""]
|
||||
reveal_type(d) # revealed: Literal[b""]
|
||||
```
|
||||
|
||||
## Unions of tuples
|
||||
|
||||
A union of a fixed-length tuple and a variable-length tuple must be collapsed to the variable-length
|
||||
element, never to the fixed-length element (`tuple[()] | tuple[Any, ...]` -> `tuple[Any, ...]`, not
|
||||
`tuple[()]`).
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
|
||||
def f(
|
||||
a: tuple[()] | tuple[int, ...],
|
||||
b: tuple[int, ...] | tuple[()],
|
||||
c: tuple[int] | tuple[str, ...],
|
||||
d: tuple[str, ...] | tuple[int],
|
||||
e: tuple[()] | tuple[Any, ...],
|
||||
f: tuple[Any, ...] | tuple[()],
|
||||
g: tuple[Any, ...] | tuple[Any | str, ...],
|
||||
h: tuple[Any | str, ...] | tuple[Any, ...],
|
||||
):
|
||||
reveal_type(a) # revealed: tuple[int, ...]
|
||||
reveal_type(b) # revealed: tuple[int, ...]
|
||||
reveal_type(c) # revealed: tuple[int] | tuple[str, ...]
|
||||
reveal_type(d) # revealed: tuple[str, ...] | tuple[int]
|
||||
reveal_type(e) # revealed: tuple[Any, ...]
|
||||
reveal_type(f) # revealed: tuple[Any, ...]
|
||||
reveal_type(g) # revealed: tuple[Any | str, ...]
|
||||
reveal_type(h) # revealed: tuple[Any | str, ...]
|
||||
```
|
||||
|
||||
## Unions of other generic containers
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
|
||||
class Bivariant[T]: ...
|
||||
|
||||
class Covariant[T]:
|
||||
def get(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
class Contravariant[T]:
|
||||
def receive(self, input: T) -> None: ...
|
||||
|
||||
class Invariant[T]:
|
||||
mutable_attribute: T
|
||||
|
||||
def _(
|
||||
a: Bivariant[Any] | Bivariant[Any | str],
|
||||
b: Bivariant[Any | str] | Bivariant[Any],
|
||||
c: Covariant[Any] | Covariant[Any | str],
|
||||
d: Covariant[Any | str] | Covariant[Any],
|
||||
e: Contravariant[Any | str] | Contravariant[Any],
|
||||
f: Contravariant[Any] | Contravariant[Any | str],
|
||||
g: Invariant[Any] | Invariant[Any | str],
|
||||
h: Invariant[Any | str] | Invariant[Any],
|
||||
):
|
||||
reveal_type(a) # revealed: Bivariant[Any]
|
||||
reveal_type(b) # revealed: Bivariant[Any | str]
|
||||
reveal_type(c) # revealed: Covariant[Any | str]
|
||||
reveal_type(d) # revealed: Covariant[Any | str]
|
||||
reveal_type(e) # revealed: Contravariant[Any]
|
||||
reveal_type(f) # revealed: Contravariant[Any]
|
||||
reveal_type(g) # revealed: Invariant[Any] | Invariant[Any | str]
|
||||
reveal_type(h) # revealed: Invariant[Any | str] | Invariant[Any]
|
||||
```
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue