mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-19 20:24:27 +00:00
[ty] Update "constraint implication" relation to work on constraints between two typevars (#21068)
It's possible for a constraint to mention two typevars. For instance, in the body of ```py def f[S: int, T: S](): ... ``` the baseline constraint set would be `(T ≤ S) ∧ (S ≤ int)`. That is, `S` must specialize to some subtype of `int`, and `T` must specialize to a subtype of the type that `S` specializes to. This PR updates the new "constraint implication" relationship from #21010 to work on these kinds of constraint sets. For instance, in the example above, we should be able to see that `T ≤ int` must always hold: ```py def f[S, T](): constraints = ConstraintSet.range(Never, S, int) & ConstraintSet.range(Never, T, S) static_assert(constraints.implies_subtype_of(T, int)) # now succeeds! ``` This did not require major changes to the implementation of `implies_subtype_of`. That method already relies on how our `simplify` and `domain` methods expand a constraint set to include the transitive closure of the constraints that it mentions, and to mark certain combinations of constraints as impossible. Previously, that transitive closure logic only looked at pairs of constraints that constrain the same typevar. (For instance, to notice that `(T ≤ bool) ∧ ¬(T ≤ int)` is impossible.) Now we also look at pairs of constraints that constraint different typevars, if one of the constraints is bound by the other — that is, pairs of the form `T ≤ S` and `S ≤ something`, or `S ≤ T` and `something ≤ S`. In those cases, transitivity lets us add a new derived constraint that `T ≤ something` or `something ≤ T`, respectively. Having done that, our existing `implies_subtype_of` logic finds and takes into account that derived constraint.
This commit is contained in:
parent
1c7ea690a8
commit
c0b04d4b7c
2 changed files with 89 additions and 28 deletions
|
|
@ -180,16 +180,12 @@ This might require propagating constraints from other typevars.
|
|||
def mutually_constrained[T, U]():
|
||||
# If [T = U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
||||
given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(Never, U, int)
|
||||
# TODO: no static-assert-error
|
||||
# error: [static-assert-error]
|
||||
static_assert(given_int.implies_subtype_of(T, int))
|
||||
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||
static_assert(not given_int.implies_subtype_of(T, str))
|
||||
|
||||
# If [T ≤ U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
||||
given_int = ConstraintSet.range(Never, T, U) & ConstraintSet.range(Never, U, int)
|
||||
# TODO: no static-assert-error
|
||||
# error: [static-assert-error]
|
||||
static_assert(given_int.implies_subtype_of(T, int))
|
||||
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||
static_assert(not given_int.implies_subtype_of(T, str))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue