mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
![]() Previously, we used a very fine-grained representation for individual constraints: each constraint was _either_ a range constraint, a not-equivalent constraint, or an incomparable constraint. These three pieces are enough to represent all of the "real" constraints we need to create — range constraints and their negation. However, it meant that we weren't picking up as many chances to simplify constraint sets as we could. Our simplification logic depends on being able to look at _pairs_ of constraints or clauses to see if they simplify relative to each other. With our fine-grained representation, we could easily encounter situations that we should have been able to simplify, but that would require looking at three or more individual constraints. For instance, negating a range constraint would produce: ``` ¬(Base ≤ T ≤ Super) = ((T ≤ Base) ∧ (T ≠ Base)) ∨ (T ≁ Base) ∨ ((Super ≤ T) ∧ (T ≠ Super)) ∨ (T ≁ Super) ``` That is, `T` must be (strictly) less than `Base`, (strictly) greater than `Super`, or incomparable to either. If we tried to union those back together, we should get `always`, since `x ∨ ¬x` should always be true, no matter what `x` is. But instead we would get: ``` (Base ≤ T ≤ Super) ∨ ((T ≤ Base) ∧ (T ≠ Base)) ∨ (T ≁ Base) ∨ ((Super ≤ T) ∧ (T ≠ Super)) ∨ (T ≁ Super) ``` Nothing would simplify relative to each other, because we'd have to look at all five union elements to see that together they do in fact combine to `always`. The fine-grained representation was nice, because it made it easier to [work out the math](https://dcreager.net/theory/constraints/) for intersections and unions of each kind of constraint. But being able to simplify is more important, since the example above comes up immediately in #20093 when trying to handle constrained typevars. The fix in this PR is to go back to a more coarse-grained representation, where each individual constraint consists of a positive range (which might be `always` / `Never ≤ T ≤ object`), and zero or more negative ranges. The intuition is to think of a constraint as a region of the type space (representable as a range) with zero or more "holes" removed from it. With this representation, negating a range constraint produces: ``` ¬(Base ≤ T ≤ Super) = (always ∧ ¬(Base ≤ T ≤ Super)) ``` (That looks trivial, because it is! We just move the positive range to the negative side.) The math is not that much harder than before, because there are only three combinations to consider (each for intersection and union) — though the fact that there can be multiple holes in a constraint does require some nested loops. But the mdtest suite gives me confidence that this is not introducing any new issues, and it definitely removes a troublesome TODO. (As an aside, this change also means that we are back to having each clause contain no more than one individual constraint for any typevar. This turned out to be important, because part of our simplification logic was also depending on that!) --------- Co-authored-by: Carl Meyer <carl@astral.sh> |
||
---|---|---|
.. | ||
src | ||
ty_extensions | ||
vendor/typeshed | ||
.gitignore | ||
build.rs | ||
Cargo.toml | ||
README.md |
Vendored types for the stdlib
This crate vendors typeshed's stubs for the standard library. The vendored stubs can be found in crates/ty_vendored/vendor/typeshed
. The file crates/ty_vendored/vendor/typeshed/source_commit.txt
tells you the typeshed commit that our vendored stdlib stubs currently correspond to.
The typeshed stubs are updated every two weeks via an automated PR using the sync_typeshed.yaml
workflow in the .github/workflows
directory. This workflow can also be triggered at any time via workflow dispatch.