mirror of https://github.com/astral-sh/ruff
unwoof
This commit is contained in:
parent
dedfa8a642
commit
acd08168c8
|
|
@ -60,29 +60,29 @@ class Sub(Base): ...
|
||||||
class Unrelated: ...
|
class Unrelated: ...
|
||||||
|
|
||||||
def unbounded[T]():
|
def unbounded[T]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
|
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
|
||||||
static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
|
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
|
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
|
||||||
static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
|
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
|
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
|
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
|
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
|
||||||
static_assert(ConstraintSet.range(Never, T, Sub).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Sub).
|
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Sub).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
|
||||||
```
|
```
|
||||||
|
|
@ -106,38 +106,38 @@ class Sub(Base): ...
|
||||||
class Unrelated: ...
|
class Unrelated: ...
|
||||||
|
|
||||||
def bounded[T: Base]():
|
def bounded[T: Base]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
|
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
|
||||||
static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# Every valid specialization satisfies (T ≤ Base). Since (Base ≤ Super), every valid
|
# Every valid specialization satisfies (T ≤ Base). Since (Base ≤ Super), every valid
|
||||||
# specialization also satisfies (T ≤ Super).
|
# specialization also satisfies (T ≤ Super).
|
||||||
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
|
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# Every valid specialization satisfies (T ≤ Base).
|
# Every valid specialization satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
|
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
|
||||||
static_assert(ConstraintSet.range(Never, T, Sub).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub).
|
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
|
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
|
||||||
constraints = ConstraintSet.range(Never, T, Unrelated)
|
constraints = ConstraintSet.range(Never, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
|
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
# Never is the only type that satisfies both (T ≤ Base) and (T ≤ Unrelated). So there is no
|
# Never is the only type that satisfies both (T ≤ Base) and (T ≤ Unrelated). So there is no
|
||||||
# valid specialization that satisfies (T ≤ Unrelated ∧ T ≠ Never).
|
# valid specialization that satisfies (T ≤ Unrelated ∧ T ≠ Never).
|
||||||
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
||||||
static_assert(not constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -153,15 +153,15 @@ the constraint set.
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
def bounded_by_gradual[T: Any]():
|
def bounded_by_gradual[T: Any]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose Base as the materialization for the upper bound, then (T = Base) is a valid
|
# If we choose Base as the materialization for the upper bound, then (T = Base) is a valid
|
||||||
# specialization, which satisfies (T ≤ Base).
|
# specialization, which satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# We are free to choose any materialization of the upper bound, and only have to show that the
|
# We are free to choose any materialization of the upper bound, and only have to show that the
|
||||||
# constraint set holds for that one materialization. Having chosen one materialization, we then
|
# constraint set holds for that one materialization. Having chosen one materialization, we then
|
||||||
# have to show that the constraint set holds for all valid specializations of that
|
# have to show that the constraint set holds for all valid specializations of that
|
||||||
|
|
@ -173,7 +173,7 @@ def bounded_by_gradual[T: Any]():
|
||||||
# If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization,
|
# If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization,
|
||||||
# which satisfies (T ≤ Unrelated).
|
# which satisfies (T ≤ Unrelated).
|
||||||
constraints = ConstraintSet.range(Never, T, Unrelated)
|
constraints = ConstraintSet.range(Never, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Never as the materialization, then (T = Never) is the only valid specialization,
|
# If we choose Never as the materialization, then (T = Never) is the only valid specialization,
|
||||||
# which satisfies (T ≤ Unrelated).
|
# which satisfies (T ≤ Unrelated).
|
||||||
static_assert(constraints.satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars())
|
||||||
|
|
@ -181,7 +181,7 @@ def bounded_by_gradual[T: Any]():
|
||||||
# If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization,
|
# If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization,
|
||||||
# which satisfies (T ≤ Unrelated ∧ T ≠ Never).
|
# which satisfies (T ≤ Unrelated ∧ T ≠ Never).
|
||||||
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# There is no upper bound that we can choose to satisfy this constraint set in non-inferable
|
# There is no upper bound that we can choose to satisfy this constraint set in non-inferable
|
||||||
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
||||||
# (T ≤ Unrelated ∧ T ≠ Never).
|
# (T ≤ Unrelated ∧ T ≠ Never).
|
||||||
|
|
@ -196,15 +196,15 @@ restrictive variance (i.e., invariance), but we get the same results for other v
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def bounded_by_gradual[T: list[Any]]():
|
def bounded_by_gradual[T: list[Any]]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Base] as the materialization of the upper bound, then (T = list[Base]) is a
|
# If we choose list[Base] as the materialization of the upper bound, then (T = list[Base]) is a
|
||||||
# valid specialization, which satisfies (T ≤ list[Base]).
|
# valid specialization, which satisfies (T ≤ list[Base]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Base]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Base as the materialization, then all valid specializations must satisfy
|
# If we choose Base as the materialization, then all valid specializations must satisfy
|
||||||
# (T ≤ list[Base]).
|
# (T ≤ list[Base]).
|
||||||
# We are free to choose any materialization of the upper bound, and only have to show that the
|
# We are free to choose any materialization of the upper bound, and only have to show that the
|
||||||
|
|
@ -217,7 +217,7 @@ def bounded_by_gradual[T: list[Any]]():
|
||||||
# If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid
|
# If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Unrelated]).
|
# specialization, which satisfies (T ≤ list[Unrelated]).
|
||||||
constraints = ConstraintSet.range(Never, T, list[Unrelated])
|
constraints = ConstraintSet.range(Never, T, list[Unrelated])
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Unrelated as the materialization, then all valid specializations must satisfy
|
# If we choose Unrelated as the materialization, then all valid specializations must satisfy
|
||||||
# (T ≤ list[Unrelated]).
|
# (T ≤ list[Unrelated]).
|
||||||
static_assert(constraints.satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars())
|
||||||
|
|
@ -225,7 +225,7 @@ def bounded_by_gradual[T: list[Any]]():
|
||||||
# If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid
|
# If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
|
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
|
||||||
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# There is no upper bound that we can choose to satisfy this constraint set in non-inferable
|
# There is no upper bound that we can choose to satisfy this constraint set in non-inferable
|
||||||
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
||||||
# (T ≤ list[Unrelated] ∧ T ≠ Never).
|
# (T ≤ list[Unrelated] ∧ T ≠ Never).
|
||||||
|
|
@ -251,61 +251,61 @@ class Sub(Base): ...
|
||||||
class Unrelated: ...
|
class Unrelated: ...
|
||||||
|
|
||||||
def constrained[T: (Base, Unrelated)]():
|
def constrained[T: (Base, Unrelated)]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Unrelated).
|
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Unrelated).
|
||||||
static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
|
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
|
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
|
||||||
static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
|
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
|
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
|
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# Neither (T = Base) nor (T = Unrelated) satisfy (T ≤ Sub).
|
# Neither (T = Base) nor (T = Unrelated) satisfy (T ≤ Sub).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Sub).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Super ∨ T ≤ Unrelated).
|
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Super ∨ T ≤ Unrelated).
|
||||||
constraints = ConstraintSet.range(Never, T, Super) | ConstraintSet.range(Never, T, Unrelated)
|
constraints = ConstraintSet.range(Never, T, Super) | ConstraintSet.range(Never, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(constraints.satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Base ∨ T ≤ Unrelated).
|
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Base ∨ T ≤ Unrelated).
|
||||||
constraints = ConstraintSet.range(Never, T, Base) | ConstraintSet.range(Never, T, Unrelated)
|
constraints = ConstraintSet.range(Never, T, Base) | ConstraintSet.range(Never, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(constraints.satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Sub ∨ T ≤ Unrelated).
|
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Sub ∨ T ≤ Unrelated).
|
||||||
constraints = ConstraintSet.range(Never, T, Sub) | ConstraintSet.range(Never, T, Unrelated)
|
constraints = ConstraintSet.range(Never, T, Sub) | ConstraintSet.range(Never, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub ∨ T ≤ Unrelated).
|
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub ∨ T ≤ Unrelated).
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Unrelated) is a valid specialization, which satisfies (T = Super ∨ T = Unrelated).
|
# (T = Unrelated) is a valid specialization, which satisfies (T = Super ∨ T = Unrelated).
|
||||||
constraints = ConstraintSet.range(Super, T, Super) | ConstraintSet.range(Unrelated, T, Unrelated)
|
constraints = ConstraintSet.range(Super, T, Super) | ConstraintSet.range(Unrelated, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T = Super ∨ T = Unrelated).
|
# (T = Base) is a valid specialization, which does not satisfy (T = Super ∨ T = Unrelated).
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Base) and (T = Unrelated) both satisfy (T = Base ∨ T = Unrelated).
|
# (T = Base) and (T = Unrelated) both satisfy (T = Base ∨ T = Unrelated).
|
||||||
constraints = ConstraintSet.range(Base, T, Base) | ConstraintSet.range(Unrelated, T, Unrelated)
|
constraints = ConstraintSet.range(Base, T, Base) | ConstraintSet.range(Unrelated, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(constraints.satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
# (T = Unrelated) is a valid specialization, which satisfies (T = Sub ∨ T = Unrelated).
|
# (T = Unrelated) is a valid specialization, which satisfies (T = Sub ∨ T = Unrelated).
|
||||||
constraints = ConstraintSet.range(Sub, T, Sub) | ConstraintSet.range(Unrelated, T, Unrelated)
|
constraints = ConstraintSet.range(Sub, T, Sub) | ConstraintSet.range(Unrelated, T, Unrelated)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# (T = Base) is a valid specialization, which does not satisfy (T = Sub ∨ T = Unrelated).
|
# (T = Base) is a valid specialization, which does not satisfy (T = Sub ∨ T = Unrelated).
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
```
|
```
|
||||||
|
|
@ -322,50 +322,50 @@ satisfy the constraint set.
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
def constrained_by_gradual[T: (Base, Any)]():
|
def constrained_by_gradual[T: (Base, Any)]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose Unrelated as the materialization of the gradual constraint, then (T = Unrelated)
|
# If we choose Unrelated as the materialization of the gradual constraint, then (T = Unrelated)
|
||||||
# is a valid specialization, which satisfies (T ≤ Unrelated).
|
# is a valid specialization, which satisfies (T ≤ Unrelated).
|
||||||
static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = Base) is a valid specialization, which does
|
# No matter which materialization we choose, (T = Base) is a valid specialization, which does
|
||||||
# not satisfy (T ≤ Unrelated).
|
# not satisfy (T ≤ Unrelated).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose Super as the materialization, then (T = Super) is a valid specialization, which
|
# If we choose Super as the materialization, then (T = Super) is a valid specialization, which
|
||||||
# satisfies (T ≤ Super).
|
# satisfies (T ≤ Super).
|
||||||
static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid
|
# If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid
|
||||||
# specializations, both of which satisfy (T ≤ Super).
|
# specializations, both of which satisfy (T ≤ Super).
|
||||||
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose Base as the materialization, then (T = Base) is a valid specialization, which
|
# If we choose Base as the materialization, then (T = Base) is a valid specialization, which
|
||||||
# satisfies (T ≤ Base).
|
# satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid
|
# If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid
|
||||||
# specializations, both of which satisfy (T ≤ Base).
|
# specializations, both of which satisfy (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
||||||
|
|
||||||
def constrained_by_two_gradual[T: (Any, Any)]():
|
def constrained_by_two_gradual[T: (Any, Any)]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose Unrelated as the materialization of either constraint, then (T = Unrelated) is a
|
# If we choose Unrelated as the materialization of either constraint, then (T = Unrelated) is a
|
||||||
# valid specialization, which satisfies (T ≤ Unrelated).
|
# valid specialization, which satisfies (T ≤ Unrelated).
|
||||||
static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Unrelated as the materialization of both constraints, then (T = Unrelated) is the
|
# If we choose Unrelated as the materialization of both constraints, then (T = Unrelated) is the
|
||||||
# only valid specialization, which satisfies (T ≤ Unrelated).
|
# only valid specialization, which satisfies (T ≤ Unrelated).
|
||||||
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose Base as the materialization of either constraint, then (T = Base) is a valid
|
# If we choose Base as the materialization of either constraint, then (T = Base) is a valid
|
||||||
# specialization, which satisfies (T ≤ Base).
|
# specialization, which satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Never as the materialization of both constraints, then (T = Never) is the only
|
# If we choose Never as the materialization of both constraints, then (T = Never) is the only
|
||||||
# valid specialization, which satisfies (T ≤ Base).
|
# valid specialization, which satisfies (T ≤ Base).
|
||||||
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
|
||||||
|
|
@ -379,35 +379,35 @@ restrictive variance (i.e., invariance), but we get the same results for other v
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def constrained_by_gradual[T: (list[Base], list[Any])]():
|
def constrained_by_gradual[T: (list[Base], list[Any])]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# No matter which materialization we choose, every valid specialization will be of the form
|
# No matter which materialization we choose, every valid specialization will be of the form
|
||||||
# (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore
|
# (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore
|
||||||
# no materialization or specialization that satisfies (T ≤ Unrelated).
|
# no materialization or specialization that satisfies (T ≤ Unrelated).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Super] as the materialization, then (T = list[Super]) is a valid
|
# If we choose list[Super] as the materialization, then (T = list[Super]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Super]).
|
# specialization, which satisfies (T ≤ list[Super]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Super]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
||||||
# does not satisfy (T ≤ list[Super]).
|
# does not satisfy (T ≤ list[Super]).
|
||||||
static_assert(not ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Base] as the materialization, then (T = list[Base]) is a valid
|
# If we choose list[Base] as the materialization, then (T = list[Base]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Base]).
|
# specialization, which satisfies (T ≤ list[Base]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Base]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose list[Base] as the materialization, then all valid specializations must satisfy
|
# If we choose list[Base] as the materialization, then all valid specializations must satisfy
|
||||||
# (T ≤ list[Base]).
|
# (T ≤ list[Base]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization,
|
# If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization,
|
||||||
# which # satisfies (T ≤ list[Sub]).
|
# which # satisfies (T ≤ list[Sub]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Sub]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
||||||
# does not satisfy (T ≤ list[Sub]).
|
# does not satisfy (T ≤ list[Sub]).
|
||||||
static_assert(not ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars())
|
||||||
|
|
@ -415,7 +415,7 @@ def constrained_by_gradual[T: (list[Base], list[Any])]():
|
||||||
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Unrelated]).
|
# specialization, which satisfies (T ≤ list[Unrelated]).
|
||||||
constraints = ConstraintSet.range(Never, T, list[Unrelated])
|
constraints = ConstraintSet.range(Never, T, list[Unrelated])
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
||||||
# does not satisfy (T ≤ list[Unrelated]).
|
# does not satisfy (T ≤ list[Unrelated]).
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
|
|
@ -423,42 +423,42 @@ def constrained_by_gradual[T: (list[Base], list[Any])]():
|
||||||
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
|
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
|
||||||
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# There is no materialization that we can choose to satisfy this constraint set in non-inferable
|
# There is no materialization that we can choose to satisfy this constraint set in non-inferable
|
||||||
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
||||||
# (T ≤ list[Unrelated] ∧ T ≠ Never).
|
# (T ≤ list[Unrelated] ∧ T ≠ Never).
|
||||||
static_assert(not constraints.satisfied_by_all_typevars())
|
static_assert(not constraints.satisfied_by_all_typevars())
|
||||||
|
|
||||||
def constrained_by_two_gradual[T: (list[Any], list[Any])]():
|
def constrained_by_two_gradual[T: (list[Any], list[Any])]():
|
||||||
static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
|
||||||
|
|
||||||
static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
|
||||||
|
|
||||||
# No matter which materialization we choose, every valid specialization will be of the form
|
# No matter which materialization we choose, every valid specialization will be of the form
|
||||||
# (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore
|
# (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore
|
||||||
# no materialization or specialization that satisfies (T ≤ Unrelated).
|
# no materialization or specialization that satisfies (T ≤ Unrelated).
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Super] as the materialization, then (T = list[Super]) is a valid
|
# If we choose list[Super] as the materialization, then (T = list[Super]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Super]).
|
# specialization, which satisfies (T ≤ list[Super]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Super]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
||||||
# does not satisfy (T ≤ list[Super]).
|
# does not satisfy (T ≤ list[Super]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Base] as the materialization, then (T = list[Base]) is a valid
|
# If we choose list[Base] as the materialization, then (T = list[Base]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Base]).
|
# specialization, which satisfies (T ≤ list[Base]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Base]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# If we choose Base as the materialization, then all valid specializations must satisfy
|
# If we choose Base as the materialization, then all valid specializations must satisfy
|
||||||
# (T ≤ list[Base]).
|
# (T ≤ list[Base]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars())
|
||||||
|
|
||||||
# If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization,
|
# If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization,
|
||||||
# which satisfies (T ≤ list[Sub]).
|
# which satisfies (T ≤ list[Sub]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Sub]).with_inferable(T).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
||||||
# does not satisfy (T ≤ list[Sub]).
|
# does not satisfy (T ≤ list[Sub]).
|
||||||
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars())
|
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars())
|
||||||
|
|
@ -466,7 +466,7 @@ def constrained_by_two_gradual[T: (list[Any], list[Any])]():
|
||||||
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Unrelated]).
|
# specialization, which satisfies (T ≤ list[Unrelated]).
|
||||||
constraints = ConstraintSet.range(Never, T, list[Unrelated])
|
constraints = ConstraintSet.range(Never, T, list[Unrelated])
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
|
||||||
# does not satisfy (T ≤ list[Unrelated]).
|
# does not satisfy (T ≤ list[Unrelated]).
|
||||||
static_assert(constraints.satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars())
|
||||||
|
|
@ -474,7 +474,7 @@ def constrained_by_two_gradual[T: (list[Any], list[Any])]():
|
||||||
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
|
||||||
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
|
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
|
||||||
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
|
||||||
static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
|
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
|
||||||
# There is no constraint that we can choose to satisfy this constraint set in non-inferable
|
# There is no constraint that we can choose to satisfy this constraint set in non-inferable
|
||||||
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
|
||||||
# (T ≤ list[Unrelated] ∧ T ≠ Never).
|
# (T ≤ list[Unrelated] ∧ T ≠ Never).
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -36,9 +36,9 @@ use crate::types::tuple::{TupleLength, TupleType};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundMethodType, BoundTypeVarIdentity, ClassLiteral, DataclassFlags, DataclassParams,
|
BoundMethodType, BoundTypeVarIdentity, ClassLiteral, DataclassFlags, DataclassParams,
|
||||||
FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy,
|
FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy,
|
||||||
PropertyInstanceType, SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext,
|
NominalInstanceType, PropertyInstanceType, SpecialFormType, TrackedConstraintSet,
|
||||||
UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, infer_isolated_expression,
|
TypeAliasType, TypeContext, UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support,
|
||||||
todo_type,
|
infer_isolated_expression, todo_type,
|
||||||
};
|
};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion};
|
use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion};
|
||||||
|
|
@ -181,7 +181,7 @@ impl<'db> Bindings<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.evaluate_known_cases(db, argument_types, dataclass_field_specifiers);
|
self.evaluate_known_cases(db, dataclass_field_specifiers);
|
||||||
|
|
||||||
// In order of precedence:
|
// In order of precedence:
|
||||||
//
|
//
|
||||||
|
|
@ -300,12 +300,7 @@ impl<'db> Bindings<'db> {
|
||||||
|
|
||||||
/// Evaluates the return type of certain known callables, where we have special-case logic to
|
/// Evaluates the return type of certain known callables, where we have special-case logic to
|
||||||
/// determine the return type in a way that isn't directly expressible in the type system.
|
/// determine the return type in a way that isn't directly expressible in the type system.
|
||||||
fn evaluate_known_cases(
|
fn evaluate_known_cases(&mut self, db: &'db dyn Db, dataclass_field_specifiers: &[Type<'db>]) {
|
||||||
&mut self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
argument_types: &CallArguments<'_, 'db>,
|
|
||||||
dataclass_field_specifiers: &[Type<'db>],
|
|
||||||
) {
|
|
||||||
let to_bool = |ty: &Option<Type<'_>>, default: bool| -> bool {
|
let to_bool = |ty: &Option<Type<'_>>, default: bool| -> bool {
|
||||||
if let Some(Type::BooleanLiteral(value)) = ty {
|
if let Some(Type::BooleanLiteral(value)) = ty {
|
||||||
*value
|
*value
|
||||||
|
|
@ -1147,13 +1142,7 @@ impl<'db> Bindings<'db> {
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let constraints = ConstraintSet::range(
|
let constraints = ConstraintSet::range(db, *lower, *typevar, *upper);
|
||||||
db,
|
|
||||||
*lower,
|
|
||||||
*typevar,
|
|
||||||
*upper,
|
|
||||||
InferableTypeVars::none(),
|
|
||||||
);
|
|
||||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||||
overload.set_return_type(Type::KnownInstance(
|
overload.set_return_type(Type::KnownInstance(
|
||||||
KnownInstanceType::ConstraintSet(tracked),
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
|
@ -1164,7 +1153,7 @@ impl<'db> Bindings<'db> {
|
||||||
if !overload.parameter_types().is_empty() {
|
if !overload.parameter_types().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let constraints = ConstraintSet::always(InferableTypeVars::none());
|
let constraints = ConstraintSet::from(true);
|
||||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||||
overload.set_return_type(Type::KnownInstance(
|
overload.set_return_type(Type::KnownInstance(
|
||||||
KnownInstanceType::ConstraintSet(tracked),
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
|
@ -1175,40 +1164,13 @@ impl<'db> Bindings<'db> {
|
||||||
if !overload.parameter_types().is_empty() {
|
if !overload.parameter_types().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let constraints = ConstraintSet::never(InferableTypeVars::none());
|
let constraints = ConstraintSet::from(false);
|
||||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||||
overload.set_return_type(Type::KnownInstance(
|
overload.set_return_type(Type::KnownInstance(
|
||||||
KnownInstanceType::ConstraintSet(tracked),
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetWithInferable(
|
|
||||||
tracked,
|
|
||||||
)) => {
|
|
||||||
let mut any_invalid = false;
|
|
||||||
let inferable = InferableTypeVars::from_bound_typevars(
|
|
||||||
db,
|
|
||||||
overload
|
|
||||||
.arguments_for_parameter(argument_types, 0)
|
|
||||||
.filter_map(|(_, ty)| {
|
|
||||||
let identity = ty
|
|
||||||
.as_typevar()
|
|
||||||
.map(|bound_typevar| bound_typevar.identity(db));
|
|
||||||
any_invalid |= identity.is_none();
|
|
||||||
identity
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if any_invalid {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = tracked.constraints(db).with_inferable(inferable);
|
|
||||||
let tracked = TrackedConstraintSet::new(db, result);
|
|
||||||
overload.set_return_type(Type::KnownInstance(
|
|
||||||
KnownInstanceType::ConstraintSet(tracked),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::KnownBoundMethod(
|
Type::KnownBoundMethod(
|
||||||
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked),
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked),
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -1216,7 +1178,12 @@ impl<'db> Bindings<'db> {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = ty_a.when_subtype_of_given(db, *ty_b, tracked.constraints(db));
|
let result = ty_a.when_subtype_of_given(
|
||||||
|
db,
|
||||||
|
*ty_b,
|
||||||
|
tracked.constraints(db),
|
||||||
|
InferableTypeVars::none(),
|
||||||
|
);
|
||||||
let tracked = TrackedConstraintSet::new(db, result);
|
let tracked = TrackedConstraintSet::new(db, result);
|
||||||
overload.set_return_type(Type::KnownInstance(
|
overload.set_return_type(Type::KnownInstance(
|
||||||
KnownInstanceType::ConstraintSet(tracked),
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
|
@ -1246,7 +1213,35 @@ impl<'db> Bindings<'db> {
|
||||||
Type::KnownBoundMethod(
|
Type::KnownBoundMethod(
|
||||||
KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(tracked),
|
KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(tracked),
|
||||||
) => {
|
) => {
|
||||||
let result = tracked.constraints(db).satisfied_by_all_typevars(db);
|
let extract_inferable = |instance: &NominalInstanceType<'db>| {
|
||||||
|
if instance.has_known_class(db, KnownClass::NoneType) {
|
||||||
|
// Caller explicitly passed None, so no typevars are inferable.
|
||||||
|
return Some(InferableTypeVars::none());
|
||||||
|
}
|
||||||
|
Some(InferableTypeVars::from_bound_typevars(
|
||||||
|
db,
|
||||||
|
instance.tuple_spec(db)?.fixed_elements().filter_map(|ty| {
|
||||||
|
ty.as_typevar()
|
||||||
|
.map(|bound_typevar| bound_typevar.identity(db))
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let inferable = match overload.parameter_types() {
|
||||||
|
// Caller did not provide argument, so no typevars are inferable.
|
||||||
|
[None] => InferableTypeVars::none(),
|
||||||
|
[Some(Type::NominalInstance(instance))] => {
|
||||||
|
match extract_inferable(instance) {
|
||||||
|
Some(inferable) => inferable,
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = tracked
|
||||||
|
.constraints(db)
|
||||||
|
.satisfied_by_all_typevars(db, inferable);
|
||||||
overload.set_return_type(Type::BooleanLiteral(result));
|
overload.set_return_type(Type::BooleanLiteral(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -531,8 +531,8 @@ impl<'db> ClassType<'db> {
|
||||||
other,
|
other,
|
||||||
inferable,
|
inferable,
|
||||||
TypeRelation::Subtyping,
|
TypeRelation::Subtyping,
|
||||||
&HasRelationToVisitor::from_inferable(inferable),
|
&HasRelationToVisitor::default(),
|
||||||
&IsDisjointVisitor::from_inferable(inferable),
|
&IsDisjointVisitor::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,48 +545,45 @@ impl<'db> ClassType<'db> {
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
self.iter_mro(db).when_any(db, inferable, |base| {
|
self.iter_mro(db).when_any(db, |base| {
|
||||||
match base {
|
match base {
|
||||||
ClassBase::Dynamic(_) => match relation {
|
ClassBase::Dynamic(_) => match relation {
|
||||||
TypeRelation::Subtyping
|
TypeRelation::Subtyping
|
||||||
| TypeRelation::Redundancy
|
| TypeRelation::Redundancy
|
||||||
| TypeRelation::ConstraintImplication(_) => {
|
| TypeRelation::ConstraintImplication(_) => {
|
||||||
ConstraintSet::from_bool(other.is_object(db), inferable)
|
ConstraintSet::from(other.is_object(db))
|
||||||
}
|
|
||||||
TypeRelation::Assignability => {
|
|
||||||
ConstraintSet::from_bool(!other.is_final(db), inferable)
|
|
||||||
}
|
}
|
||||||
|
TypeRelation::Assignability => ConstraintSet::from(!other.is_final(db)),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Protocol and Generic are not represented by a ClassType.
|
// Protocol and Generic are not represented by a ClassType.
|
||||||
ClassBase::Protocol | ClassBase::Generic => ConstraintSet::never(inferable),
|
ClassBase::Protocol | ClassBase::Generic => ConstraintSet::from(false),
|
||||||
|
|
||||||
ClassBase::Class(base) => match (base, other) {
|
ClassBase::Class(base) => match (base, other) {
|
||||||
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => {
|
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => {
|
||||||
ConstraintSet::from_bool(base == other, inferable)
|
ConstraintSet::from(base == other)
|
||||||
}
|
}
|
||||||
(ClassType::Generic(base), ClassType::Generic(other)) => {
|
(ClassType::Generic(base), ClassType::Generic(other)) => {
|
||||||
ConstraintSet::from_bool(base.origin(db) == other.origin(db), inferable)
|
ConstraintSet::from(base.origin(db) == other.origin(db)).and(db, || {
|
||||||
.and(db, || {
|
base.specialization(db).has_relation_to_impl(
|
||||||
base.specialization(db).has_relation_to_impl(
|
db,
|
||||||
db,
|
other.specialization(db),
|
||||||
other.specialization(db),
|
inferable,
|
||||||
inferable,
|
relation,
|
||||||
relation,
|
relation_visitor,
|
||||||
relation_visitor,
|
disjointness_visitor,
|
||||||
disjointness_visitor,
|
)
|
||||||
)
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
(ClassType::Generic(_), ClassType::NonGeneric(_))
|
(ClassType::Generic(_), ClassType::NonGeneric(_))
|
||||||
| (ClassType::NonGeneric(_), ClassType::Generic(_)) => {
|
| (ClassType::NonGeneric(_), ClassType::Generic(_)) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ClassBase::TypedDict => {
|
ClassBase::TypedDict => {
|
||||||
// TODO: Implement subclassing and assignability for TypedDicts.
|
// TODO: Implement subclassing and assignability for TypedDicts.
|
||||||
ConstraintSet::always(inferable)
|
ConstraintSet::from(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -600,28 +597,26 @@ impl<'db> ClassType<'db> {
|
||||||
visitor: &IsEquivalentVisitor<'db>,
|
visitor: &IsEquivalentVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
if self == other {
|
if self == other {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
// A non-generic class is never equivalent to a generic class.
|
// A non-generic class is never equivalent to a generic class.
|
||||||
// Two non-generic classes are only equivalent if they are equal (handled above).
|
// Two non-generic classes are only equivalent if they are equal (handled above).
|
||||||
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => {
|
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
(ClassType::Generic(this), ClassType::Generic(other)) => ConstraintSet::from_bool(
|
(ClassType::Generic(this), ClassType::Generic(other)) => {
|
||||||
this.origin(db) == other.origin(db),
|
ConstraintSet::from(this.origin(db) == other.origin(db)).and(db, || {
|
||||||
inferable,
|
this.specialization(db).is_equivalent_to_impl(
|
||||||
)
|
db,
|
||||||
.and(db, || {
|
other.specialization(db),
|
||||||
this.specialization(db).is_equivalent_to_impl(
|
inferable,
|
||||||
db,
|
visitor,
|
||||||
other.specialization(db),
|
)
|
||||||
inferable,
|
})
|
||||||
visitor,
|
}
|
||||||
)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1780,10 +1775,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
specialization: Option<Specialization<'db>>,
|
specialization: Option<Specialization<'db>>,
|
||||||
other: ClassType<'db>,
|
other: ClassType<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
ConstraintSet::from_bool(
|
ConstraintSet::from(self.is_subclass_of(db, specialization, other))
|
||||||
self.is_subclass_of(db, specialization, other),
|
|
||||||
InferableTypeVars::none(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if this class constitutes a typed dict specification (inherits from
|
/// Return `true` if this class constitutes a typed dict specification (inherits from
|
||||||
|
|
@ -4701,7 +4693,7 @@ impl KnownClass {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: ClassType<'db>,
|
other: ClassType<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
ConstraintSet::from_bool(self.is_subclass_of(db, other), InferableTypeVars::none())
|
ConstraintSet::from(self.is_subclass_of(db, other))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the module in which we should look up the definition for this class
|
/// Return the module in which we should look up the definition for this class
|
||||||
|
|
|
||||||
|
|
@ -74,41 +74,25 @@ use crate::types::{
|
||||||
pub(crate) trait OptionConstraintsExtension<T> {
|
pub(crate) trait OptionConstraintsExtension<T> {
|
||||||
/// Returns a constraint set that is always satisfiable if the option is `None`; otherwise
|
/// Returns a constraint set that is always satisfiable if the option is `None`; otherwise
|
||||||
/// applies a function to determine under what constraints the value inside of it holds.
|
/// applies a function to determine under what constraints the value inside of it holds.
|
||||||
fn when_none_or<'db>(
|
fn when_none_or<'db>(self, f: impl FnOnce(T) -> ConstraintSet<'db>) -> ConstraintSet<'db>;
|
||||||
self,
|
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
f: impl FnOnce(T) -> ConstraintSet<'db>,
|
|
||||||
) -> ConstraintSet<'db>;
|
|
||||||
|
|
||||||
/// Returns a constraint set that is never satisfiable if the option is `None`; otherwise
|
/// Returns a constraint set that is never satisfiable if the option is `None`; otherwise
|
||||||
/// applies a function to determine under what constraints the value inside of it holds.
|
/// applies a function to determine under what constraints the value inside of it holds.
|
||||||
fn when_some_and<'db>(
|
fn when_some_and<'db>(self, f: impl FnOnce(T) -> ConstraintSet<'db>) -> ConstraintSet<'db>;
|
||||||
self,
|
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
f: impl FnOnce(T) -> ConstraintSet<'db>,
|
|
||||||
) -> ConstraintSet<'db>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptionConstraintsExtension<T> for Option<T> {
|
impl<T> OptionConstraintsExtension<T> for Option<T> {
|
||||||
fn when_none_or<'db>(
|
fn when_none_or<'db>(self, f: impl FnOnce(T) -> ConstraintSet<'db>) -> ConstraintSet<'db> {
|
||||||
self,
|
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
f: impl FnOnce(T) -> ConstraintSet<'db>,
|
|
||||||
) -> ConstraintSet<'db> {
|
|
||||||
match self {
|
match self {
|
||||||
Some(value) => f(value),
|
Some(value) => f(value),
|
||||||
None => ConstraintSet::always(inferable),
|
None => ConstraintSet::always(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn when_some_and<'db>(
|
fn when_some_and<'db>(self, f: impl FnOnce(T) -> ConstraintSet<'db>) -> ConstraintSet<'db> {
|
||||||
self,
|
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
f: impl FnOnce(T) -> ConstraintSet<'db>,
|
|
||||||
) -> ConstraintSet<'db> {
|
|
||||||
match self {
|
match self {
|
||||||
Some(value) => f(value),
|
Some(value) => f(value),
|
||||||
None => ConstraintSet::never(inferable),
|
None => ConstraintSet::never(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +107,6 @@ pub(crate) trait IteratorConstraintsExtension<T> {
|
||||||
fn when_any<'db>(
|
fn when_any<'db>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
f: impl FnMut(T) -> ConstraintSet<'db>,
|
f: impl FnMut(T) -> ConstraintSet<'db>,
|
||||||
) -> ConstraintSet<'db>;
|
) -> ConstraintSet<'db>;
|
||||||
|
|
||||||
|
|
@ -135,7 +118,6 @@ pub(crate) trait IteratorConstraintsExtension<T> {
|
||||||
fn when_all<'db>(
|
fn when_all<'db>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
f: impl FnMut(T) -> ConstraintSet<'db>,
|
f: impl FnMut(T) -> ConstraintSet<'db>,
|
||||||
) -> ConstraintSet<'db>;
|
) -> ConstraintSet<'db>;
|
||||||
}
|
}
|
||||||
|
|
@ -147,10 +129,9 @@ where
|
||||||
fn when_any<'db>(
|
fn when_any<'db>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
mut f: impl FnMut(T) -> ConstraintSet<'db>,
|
mut f: impl FnMut(T) -> ConstraintSet<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
let mut result = ConstraintSet::never(inferable);
|
let mut result = ConstraintSet::never();
|
||||||
for child in self {
|
for child in self {
|
||||||
if result.union(db, f(child)).is_always_satisfied(db) {
|
if result.union(db, f(child)).is_always_satisfied(db) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -162,10 +143,9 @@ where
|
||||||
fn when_all<'db>(
|
fn when_all<'db>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
mut f: impl FnMut(T) -> ConstraintSet<'db>,
|
mut f: impl FnMut(T) -> ConstraintSet<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::always();
|
||||||
for child in self {
|
for child in self {
|
||||||
if result.intersect(db, f(child)).is_never_satisfied(db) {
|
if result.intersect(db, f(child)).is_never_satisfied(db) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -184,34 +164,18 @@ where
|
||||||
pub struct ConstraintSet<'db> {
|
pub struct ConstraintSet<'db> {
|
||||||
/// The BDD representing this constraint set
|
/// The BDD representing this constraint set
|
||||||
node: Node<'db>,
|
node: Node<'db>,
|
||||||
|
|
||||||
/// The typevars that were inferable when constructing this constraint set
|
|
||||||
pub(crate) inferable: InferableTypeVars<'db>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> ConstraintSet<'db> {
|
impl<'db> ConstraintSet<'db> {
|
||||||
pub(crate) fn never(inferable: InferableTypeVars<'db>) -> Self {
|
fn never() -> Self {
|
||||||
Self {
|
Self {
|
||||||
node: Node::AlwaysFalse,
|
node: Node::AlwaysFalse,
|
||||||
inferable,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn always(inferable: InferableTypeVars<'db>) -> Self {
|
fn always() -> Self {
|
||||||
Self {
|
Self {
|
||||||
node: Node::AlwaysTrue,
|
node: Node::AlwaysTrue,
|
||||||
inferable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_bool(b: bool, inferable: InferableTypeVars<'db>) -> Self {
|
|
||||||
Self {
|
|
||||||
node: if b {
|
|
||||||
Node::AlwaysTrue
|
|
||||||
} else {
|
|
||||||
Node::AlwaysFalse
|
|
||||||
},
|
|
||||||
inferable,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,7 +185,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
typevar: BoundTypeVarInstance<'db>,
|
typevar: BoundTypeVarInstance<'db>,
|
||||||
lower: Type<'db>,
|
lower: Type<'db>,
|
||||||
upper: Type<'db>,
|
upper: Type<'db>,
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
relation: TypeRelation<'db>,
|
relation: TypeRelation<'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (lower, upper) = match relation {
|
let (lower, upper) = match relation {
|
||||||
|
|
@ -239,14 +202,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
node: ConstrainedTypeVar::new_node(db, typevar, lower, upper),
|
node: ConstrainedTypeVar::new_node(db, typevar, lower, upper),
|
||||||
inferable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn with_inferable(self, inferable: InferableTypeVars<'db>) -> Self {
|
|
||||||
Self {
|
|
||||||
node: self.node,
|
|
||||||
inferable,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +226,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node: self.node.when_subtype_of_given(db, lhs, rhs),
|
node: self.node.when_subtype_of_given(db, lhs, rhs),
|
||||||
inferable: self.inferable,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,8 +243,12 @@ impl<'db> ConstraintSet<'db> {
|
||||||
/// since the constraint set cannot be affected by any typevars that it does not mention. That
|
/// since the constraint set cannot be affected by any typevars that it does not mention. That
|
||||||
/// means that those additional typevars trivially satisfy the constraint set, regardless of
|
/// means that those additional typevars trivially satisfy the constraint set, regardless of
|
||||||
/// whether they are inferable or not.
|
/// whether they are inferable or not.
|
||||||
pub(crate) fn satisfied_by_all_typevars(self, db: &'db dyn Db) -> bool {
|
pub(crate) fn satisfied_by_all_typevars(
|
||||||
self.node.satisfied_by_all_typevars(db, self.inferable)
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
inferable: InferableTypeVars<'db>,
|
||||||
|
) -> bool {
|
||||||
|
self.node.satisfied_by_all_typevars(db, inferable)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates this constraint set to hold the union of itself and another constraint set.
|
/// Updates this constraint set to hold the union of itself and another constraint set.
|
||||||
|
|
@ -301,7 +259,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
/// only mention typevars that are inferable in the lhs, or which both sides consider
|
/// only mention typevars that are inferable in the lhs, or which both sides consider
|
||||||
/// non-inferable.
|
/// non-inferable.
|
||||||
pub(crate) fn union(&mut self, db: &'db dyn Db, other: Self) -> Self {
|
pub(crate) fn union(&mut self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
let other = other.reduce_inferable(db, self.inferable);
|
|
||||||
self.node = self.node.or(db, other.node);
|
self.node = self.node.or(db, other.node);
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +271,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
/// only mention typevars that are inferable in the lhs, or which both sides consider
|
/// only mention typevars that are inferable in the lhs, or which both sides consider
|
||||||
/// non-inferable.
|
/// non-inferable.
|
||||||
pub(crate) fn intersect(&mut self, db: &'db dyn Db, other: Self) -> Self {
|
pub(crate) fn intersect(&mut self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
let other = other.reduce_inferable(db, self.inferable);
|
|
||||||
self.node = self.node.and(db, other.node);
|
self.node = self.node.and(db, other.node);
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +279,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
pub(crate) fn negate(self, db: &'db dyn Db) -> Self {
|
pub(crate) fn negate(self, db: &'db dyn Db) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node: self.node.negate(db),
|
node: self.node.negate(db),
|
||||||
inferable: self.inferable,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,7 +310,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
pub(crate) fn iff(self, db: &'db dyn Db, other: Self) -> Self {
|
pub(crate) fn iff(self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
ConstraintSet {
|
ConstraintSet {
|
||||||
node: self.node.iff(db, other.node),
|
node: self.node.iff(db, other.node),
|
||||||
inferable: self.inferable.merge(db, other.inferable),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,14 +318,14 @@ impl<'db> ConstraintSet<'db> {
|
||||||
/// away. (That is, those typevars will be removed from the constraint set, and the constraint
|
/// away. (That is, those typevars will be removed from the constraint set, and the constraint
|
||||||
/// set will return true whenever there was _any_ specialization of those typevars that
|
/// set will return true whenever there was _any_ specialization of those typevars that
|
||||||
/// returned true before.)
|
/// returned true before.)
|
||||||
|
/// XXX: fix docs
|
||||||
pub(crate) fn reduce_inferable(
|
pub(crate) fn reduce_inferable(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
inferable: InferableTypeVars<'db>,
|
to_remove: impl IntoIterator<Item = BoundTypeVarIdentity<'db>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let to_abstract = self.inferable.subtract(db, inferable);
|
let node = self.node.exists(db, to_remove);
|
||||||
let node = self.node.exists(db, to_abstract);
|
Self { node }
|
||||||
Self { node, inferable }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn range(
|
pub(crate) fn range(
|
||||||
|
|
@ -379,16 +333,8 @@ impl<'db> ConstraintSet<'db> {
|
||||||
lower: Type<'db>,
|
lower: Type<'db>,
|
||||||
typevar: BoundTypeVarInstance<'db>,
|
typevar: BoundTypeVarInstance<'db>,
|
||||||
upper: Type<'db>,
|
upper: Type<'db>,
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::constrain_typevar(
|
Self::constrain_typevar(db, typevar, lower, upper, TypeRelation::Assignability)
|
||||||
db,
|
|
||||||
typevar,
|
|
||||||
lower,
|
|
||||||
upper,
|
|
||||||
inferable,
|
|
||||||
TypeRelation::Assignability,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
|
pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
|
||||||
|
|
@ -396,6 +342,12 @@ impl<'db> ConstraintSet<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<bool> for ConstraintSet<'_> {
|
||||||
|
fn from(b: bool) -> Self {
|
||||||
|
if b { Self::always() } else { Self::never() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'db> BoundTypeVarInstance<'db> {
|
impl<'db> BoundTypeVarInstance<'db> {
|
||||||
/// Returns whether this typevar can be the lower or upper bound of another typevar in a
|
/// Returns whether this typevar can be the lower or upper bound of another typevar in a
|
||||||
/// constraint set.
|
/// constraint set.
|
||||||
|
|
@ -2230,10 +2182,10 @@ mod tests {
|
||||||
let u = BoundTypeVarInstance::synthetic(&db, "U", TypeVarVariance::Invariant);
|
let u = BoundTypeVarInstance::synthetic(&db, "U", TypeVarVariance::Invariant);
|
||||||
let bool_type = KnownClass::Bool.to_instance(&db);
|
let bool_type = KnownClass::Bool.to_instance(&db);
|
||||||
let str_type = KnownClass::Str.to_instance(&db);
|
let str_type = KnownClass::Str.to_instance(&db);
|
||||||
let t_str = ConstraintSet::range(&db, str_type, t, str_type, InferableTypeVars::none());
|
let t_str = ConstraintSet::range(&db, str_type, t, str_type);
|
||||||
let t_bool = ConstraintSet::range(&db, bool_type, t, bool_type, InferableTypeVars::none());
|
let t_bool = ConstraintSet::range(&db, bool_type, t, bool_type);
|
||||||
let u_str = ConstraintSet::range(&db, str_type, u, str_type, InferableTypeVars::none());
|
let u_str = ConstraintSet::range(&db, str_type, u, str_type);
|
||||||
let u_bool = ConstraintSet::range(&db, bool_type, u, bool_type, InferableTypeVars::none());
|
let u_bool = ConstraintSet::range(&db, bool_type, u, bool_type);
|
||||||
let constraints = (t_str.or(&db, || t_bool)).and(&db, || u_str.or(&db, || u_bool));
|
let constraints = (t_str.or(&db, || t_bool)).and(&db, || u_str.or(&db, || u_bool));
|
||||||
let actual = constraints.node.display_graph(&db, &"").to_string();
|
let actual = constraints.node.display_graph(&db, &"").to_string();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
|
||||||
|
|
@ -532,9 +532,6 @@ impl Display for DisplayRepresentation<'_> {
|
||||||
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => {
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => {
|
||||||
f.write_str("bound method `ConstraintSet.never`")
|
f.write_str("bound method `ConstraintSet.never`")
|
||||||
}
|
}
|
||||||
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetWithInferable(_)) => {
|
|
||||||
f.write_str("bound method `ConstraintSet.with_inferable`")
|
|
||||||
}
|
|
||||||
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)) => {
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)) => {
|
||||||
f.write_str("bound method `ConstraintSet.implies_subtype_of`")
|
f.write_str("bound method `ConstraintSet.implies_subtype_of`")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -986,11 +986,11 @@ impl<'db> FunctionType<'db> {
|
||||||
| TypeRelation::ConstraintImplication(_)
|
| TypeRelation::ConstraintImplication(_)
|
||||||
) && self.normalized(db) == other.normalized(db)
|
) && self.normalized(db) == other.normalized(db)
|
||||||
{
|
{
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.literal(db) != other.literal(db) {
|
if self.literal(db) != other.literal(db) {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_signature = self.signature(db);
|
let self_signature = self.signature(db);
|
||||||
|
|
@ -1013,10 +1013,10 @@ impl<'db> FunctionType<'db> {
|
||||||
visitor: &IsEquivalentVisitor<'db>,
|
visitor: &IsEquivalentVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
if self.normalized(db) == other.normalized(db) {
|
if self.normalized(db) == other.normalized(db) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
if self.literal(db) != other.literal(db) {
|
if self.literal(db) != other.literal(db) {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
let self_signature = self.signature(db);
|
let self_signature = self.signature(db);
|
||||||
let other_signature = other.signature(db);
|
let other_signature = other.signature(db);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::cell::RefCell;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use itertools::{Either, EitherOrBoth, Itertools};
|
use itertools::Itertools;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
|
@ -119,7 +119,7 @@ pub(crate) fn typing_self<'db>(
|
||||||
.map(Type::TypeVar)
|
.map(Type::TypeVar)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) struct InferableTypeVars<'db> {
|
pub(crate) struct InferableTypeVars<'db> {
|
||||||
inner: Option<InferableTypeVarsInner<'db>>,
|
inner: Option<InferableTypeVarsInner<'db>>,
|
||||||
}
|
}
|
||||||
|
|
@ -168,36 +168,19 @@ impl<'db> InferableTypeVars<'db> {
|
||||||
(None, None) => self,
|
(None, None) => self,
|
||||||
(Some(_), None) => self,
|
(Some(_), None) => self,
|
||||||
(None, Some(_)) => other,
|
(None, Some(_)) => other,
|
||||||
(Some(self_inner), Some(other_inner)) if self_inner == other_inner => self,
|
|
||||||
(Some(self_inner), Some(other_inner)) => InferableTypeVars {
|
(Some(self_inner), Some(other_inner)) => InferableTypeVars {
|
||||||
inner: Some(self_inner.merge(db, other_inner)),
|
inner: Some(self_inner.merge(db, other_inner)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of the typevars that are in this inferable set but not in another.
|
pub(crate) fn iter(
|
||||||
pub(crate) fn subtract(
|
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
|
||||||
) -> impl Iterator<Item = BoundTypeVarIdentity<'db>> + 'db {
|
) -> impl Iterator<Item = BoundTypeVarIdentity<'db>> + 'db {
|
||||||
let (self_inner, other_inner) = match (self.inner, other.inner) {
|
self.inner
|
||||||
(None, _) => return Either::Left(Either::Left(std::iter::empty())),
|
.into_iter()
|
||||||
(Some(self_inner), None) => {
|
.flat_map(|inner| inner.inferable(db).iter().copied())
|
||||||
return Either::Left(Either::Right(self_inner.inferable(db).into_iter().copied()));
|
|
||||||
}
|
|
||||||
(Some(self_inner), Some(other_inner)) => (self_inner, other_inner),
|
|
||||||
};
|
|
||||||
let self_inferable = self_inner.inferable(db).into_iter().copied();
|
|
||||||
let other_inferable = other_inner.inferable(db).into_iter().copied();
|
|
||||||
Either::Right(
|
|
||||||
self_inferable
|
|
||||||
.merge_join_by(other_inferable, BoundTypeVarIdentity::cmp)
|
|
||||||
.filter_map(|merged| match merged {
|
|
||||||
EitherOrBoth::Left(bound_typevar) => Some(bound_typevar),
|
|
||||||
EitherOrBoth::Right(_) | EitherOrBoth::Both(_, _) => None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep this around for debugging purposes
|
// Keep this around for debugging purposes
|
||||||
|
|
@ -770,12 +753,12 @@ fn is_subtype_in_invariant_position<'db>(
|
||||||
// This should be removed and properly handled in the respective
|
// This should be removed and properly handled in the respective
|
||||||
// `(Type::TypeVar(_), _) | (_, Type::TypeVar(_))` branch of
|
// `(Type::TypeVar(_), _) | (_, Type::TypeVar(_))` branch of
|
||||||
// `Type::has_relation_to_impl`. Right now, we cannot generally
|
// `Type::has_relation_to_impl`. Right now, we cannot generally
|
||||||
// return `ConstraintSet::always(inferable)` from that branch, as that
|
// return `ConstraintSet::from(true)` from that branch, as that
|
||||||
// leads to union simplification, which means that we lose track
|
// leads to union simplification, which means that we lose track
|
||||||
// of type variables without recording the constraints under which
|
// of type variables without recording the constraints under which
|
||||||
// the relation holds.
|
// the relation holds.
|
||||||
if matches!(base, Type::TypeVar(_)) || matches!(derived, Type::TypeVar(_)) {
|
if matches!(base, Type::TypeVar(_)) || matches!(derived, Type::TypeVar(_)) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
derived.has_relation_to_impl(
|
derived.has_relation_to_impl(
|
||||||
|
|
@ -1189,7 +1172,7 @@ impl<'db> Specialization<'db> {
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
let generic_context = self.generic_context(db);
|
let generic_context = self.generic_context(db);
|
||||||
if generic_context != other.generic_context(db) {
|
if generic_context != other.generic_context(db) {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(self_tuple), Some(other_tuple)) = (self.tuple_inner(db), other.tuple_inner(db))
|
if let (Some(self_tuple), Some(other_tuple)) = (self.tuple_inner(db), other.tuple_inner(db))
|
||||||
|
|
@ -1207,7 +1190,7 @@ impl<'db> Specialization<'db> {
|
||||||
let self_materialization_kind = self.materialization_kind(db);
|
let self_materialization_kind = self.materialization_kind(db);
|
||||||
let other_materialization_kind = other.materialization_kind(db);
|
let other_materialization_kind = other.materialization_kind(db);
|
||||||
|
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::from(true);
|
||||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db))
|
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db))
|
||||||
.zip(self.types(db))
|
.zip(self.types(db))
|
||||||
.zip(other.types(db))
|
.zip(other.types(db))
|
||||||
|
|
@ -1246,7 +1229,7 @@ impl<'db> Specialization<'db> {
|
||||||
relation_visitor,
|
relation_visitor,
|
||||||
disjointness_visitor,
|
disjointness_visitor,
|
||||||
),
|
),
|
||||||
TypeVarVariance::Bivariant => ConstraintSet::always(inferable),
|
TypeVarVariance::Bivariant => ConstraintSet::from(true),
|
||||||
};
|
};
|
||||||
if result.intersect(db, compatible).is_never_satisfied(db) {
|
if result.intersect(db, compatible).is_never_satisfied(db) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1264,14 +1247,14 @@ impl<'db> Specialization<'db> {
|
||||||
visitor: &IsEquivalentVisitor<'db>,
|
visitor: &IsEquivalentVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
if self.materialization_kind(db) != other.materialization_kind(db) {
|
if self.materialization_kind(db) != other.materialization_kind(db) {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
let generic_context = self.generic_context(db);
|
let generic_context = self.generic_context(db);
|
||||||
if generic_context != other.generic_context(db) {
|
if generic_context != other.generic_context(db) {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::from(true);
|
||||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db))
|
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db))
|
||||||
.zip(self.types(db))
|
.zip(self.types(db))
|
||||||
.zip(other.types(db))
|
.zip(other.types(db))
|
||||||
|
|
@ -1288,7 +1271,7 @@ impl<'db> Specialization<'db> {
|
||||||
| TypeVarVariance::Contravariant => {
|
| TypeVarVariance::Contravariant => {
|
||||||
self_type.is_equivalent_to_impl(db, *other_type, inferable, visitor)
|
self_type.is_equivalent_to_impl(db, *other_type, inferable, visitor)
|
||||||
}
|
}
|
||||||
TypeVarVariance::Bivariant => ConstraintSet::always(inferable),
|
TypeVarVariance::Bivariant => ConstraintSet::from(true),
|
||||||
};
|
};
|
||||||
if result.intersect(db, compatible).is_never_satisfied(db) {
|
if result.intersect(db, compatible).is_never_satisfied(db) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1296,7 +1279,7 @@ impl<'db> Specialization<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match (self.tuple_inner(db), other.tuple_inner(db)) {
|
match (self.tuple_inner(db), other.tuple_inner(db)) {
|
||||||
(Some(_), None) | (None, Some(_)) => return ConstraintSet::never(inferable),
|
(Some(_), None) | (None, Some(_)) => return ConstraintSet::from(false),
|
||||||
(None, None) => {}
|
(None, None) => {}
|
||||||
(Some(self_tuple), Some(other_tuple)) => {
|
(Some(self_tuple), Some(other_tuple)) => {
|
||||||
let compatible =
|
let compatible =
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ impl<'db> Type<'db> {
|
||||||
.inner
|
.inner
|
||||||
.interface(db)
|
.interface(db)
|
||||||
.members(db)
|
.members(db)
|
||||||
.when_all(db, inferable, |member| {
|
.when_all(db, |member| {
|
||||||
member.is_satisfied_by(
|
member.is_satisfied_by(
|
||||||
db,
|
db,
|
||||||
self,
|
self,
|
||||||
|
|
@ -161,7 +161,7 @@ impl<'db> Type<'db> {
|
||||||
// recognise `str` as a subtype of `Container[str]`.
|
// recognise `str` as a subtype of `Container[str]`.
|
||||||
structurally_satisfied.or(db, || {
|
structurally_satisfied.or(db, || {
|
||||||
let Some(nominal_instance) = protocol.as_nominal_type() else {
|
let Some(nominal_instance) = protocol.as_nominal_type() else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// if `self` and `other` are *both* protocols, we also need to treat `self` as if it
|
// if `self` and `other` are *both* protocols, we also need to treat `self` as if it
|
||||||
|
|
@ -371,7 +371,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
match (self.0, other.0) {
|
match (self.0, other.0) {
|
||||||
(_, NominalInstanceInner::Object) => ConstraintSet::always(inferable),
|
(_, NominalInstanceInner::Object) => ConstraintSet::from(true),
|
||||||
(
|
(
|
||||||
NominalInstanceInner::ExactTuple(tuple1),
|
NominalInstanceInner::ExactTuple(tuple1),
|
||||||
NominalInstanceInner::ExactTuple(tuple2),
|
NominalInstanceInner::ExactTuple(tuple2),
|
||||||
|
|
@ -407,12 +407,12 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
NominalInstanceInner::ExactTuple(tuple2),
|
NominalInstanceInner::ExactTuple(tuple2),
|
||||||
) => tuple1.is_equivalent_to_impl(db, tuple2, inferable, visitor),
|
) => tuple1.is_equivalent_to_impl(db, tuple2, inferable, visitor),
|
||||||
(NominalInstanceInner::Object, NominalInstanceInner::Object) => {
|
(NominalInstanceInner::Object, NominalInstanceInner::Object) => {
|
||||||
ConstraintSet::always(inferable)
|
ConstraintSet::from(true)
|
||||||
}
|
}
|
||||||
(NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => {
|
(NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => {
|
||||||
class1.is_equivalent_to_impl(db, class2, inferable, visitor)
|
class1.is_equivalent_to_impl(db, class2, inferable, visitor)
|
||||||
}
|
}
|
||||||
_ => ConstraintSet::never(inferable),
|
_ => ConstraintSet::from(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -425,9 +425,9 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
if self.is_object() || other.is_object() {
|
if self.is_object() || other.is_object() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
let mut result = ConstraintSet::never(inferable);
|
let mut result = ConstraintSet::from(false);
|
||||||
if let Some(self_spec) = self.tuple_spec(db) {
|
if let Some(self_spec) = self.tuple_spec(db) {
|
||||||
if let Some(other_spec) = other.tuple_spec(db) {
|
if let Some(other_spec) = other.tuple_spec(db) {
|
||||||
let compatible = self_spec.is_disjoint_from_impl(
|
let compatible = self_spec.is_disjoint_from_impl(
|
||||||
|
|
@ -443,10 +443,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.or(db, || {
|
result.or(db, || {
|
||||||
ConstraintSet::from_bool(
|
ConstraintSet::from(!(self.class(db)).could_coexist_in_mro_with(db, other.class(db)))
|
||||||
!(self.class(db)).could_coexist_in_mro_with(db, other.class(db)),
|
|
||||||
inferable,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -671,8 +668,8 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||||
protocol,
|
protocol,
|
||||||
InferableTypeVars::none(),
|
InferableTypeVars::none(),
|
||||||
TypeRelation::Subtyping,
|
TypeRelation::Subtyping,
|
||||||
&HasRelationToVisitor::from_inferable(InferableTypeVars::none()),
|
&HasRelationToVisitor::default(),
|
||||||
&IsDisjointVisitor::from_inferable(InferableTypeVars::none()),
|
&IsDisjointVisitor::default(),
|
||||||
)
|
)
|
||||||
.is_always_satisfied(db)
|
.is_always_satisfied(db)
|
||||||
}
|
}
|
||||||
|
|
@ -722,17 +719,17 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'db>,
|
_inferable: InferableTypeVars<'db>,
|
||||||
_visitor: &IsEquivalentVisitor<'db>,
|
_visitor: &IsEquivalentVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
if self == other {
|
if self == other {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
let self_normalized = self.normalized(db);
|
let self_normalized = self.normalized(db);
|
||||||
if self_normalized == Type::ProtocolInstance(other) {
|
if self_normalized == Type::ProtocolInstance(other) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
ConstraintSet::from_bool(self_normalized == other.normalized(db), inferable)
|
ConstraintSet::from(self_normalized == other.normalized(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if this protocol type is disjoint from the protocol `other`.
|
/// Return `true` if this protocol type is disjoint from the protocol `other`.
|
||||||
|
|
@ -744,10 +741,10 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||||
self,
|
self,
|
||||||
_db: &'db dyn Db,
|
_db: &'db dyn Db,
|
||||||
_other: Self,
|
_other: Self,
|
||||||
inferable: InferableTypeVars<'db>,
|
_inferable: InferableTypeVars<'db>,
|
||||||
_visitor: &IsDisjointVisitor<'db>,
|
_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
|
pub(crate) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use std::collections::BTreeSet;
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
||||||
use crate::types::constraints::ConstraintSet;
|
use crate::types::constraints::ConstraintSet;
|
||||||
use crate::types::generics::InferableTypeVars;
|
|
||||||
use crate::types::{ClassType, Type, definition_expression_type, visitor};
|
use crate::types::{ClassType, Type, definition_expression_type, visitor};
|
||||||
use ruff_db::parsed::parsed_module;
|
use ruff_db::parsed::parsed_module;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
|
@ -116,37 +115,26 @@ impl<'db> NewType<'db> {
|
||||||
// Since a regular class can't inherit from a newtype, the only way for one newtype to be a
|
// Since a regular class can't inherit from a newtype, the only way for one newtype to be a
|
||||||
// subtype of another is to have the other in its chain of newtype bases. Once we reach the
|
// subtype of another is to have the other in its chain of newtype bases. Once we reach the
|
||||||
// base class, we don't have to keep looking.
|
// base class, we don't have to keep looking.
|
||||||
pub(crate) fn has_relation_to_impl(
|
pub(crate) fn has_relation_to_impl(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> {
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
) -> ConstraintSet<'db> {
|
|
||||||
if self.is_equivalent_to_impl(db, other) {
|
if self.is_equivalent_to_impl(db, other) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
for base in self.iter_bases(db) {
|
for base in self.iter_bases(db) {
|
||||||
if let NewTypeBase::NewType(base_newtype) = base {
|
if let NewTypeBase::NewType(base_newtype) = base {
|
||||||
if base_newtype.is_equivalent_to_impl(db, other) {
|
if base_newtype.is_equivalent_to_impl(db, other) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_disjoint_from_impl(
|
pub(crate) fn is_disjoint_from_impl(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> {
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
inferable: InferableTypeVars<'db>,
|
|
||||||
) -> ConstraintSet<'db> {
|
|
||||||
// Two NewTypes are disjoint if they're not equal and neither inherits from the other.
|
// Two NewTypes are disjoint if they're not equal and neither inherits from the other.
|
||||||
// NewTypes have single inheritance, and a regular class can't inherit from a NewType, so
|
// NewTypes have single inheritance, and a regular class can't inherit from a NewType, so
|
||||||
// it's not possible for some third type to multiply-inherit from both.
|
// it's not possible for some third type to multiply-inherit from both.
|
||||||
let mut self_not_subtype_of_other =
|
let mut self_not_subtype_of_other = self.has_relation_to_impl(db, other).negate(db);
|
||||||
self.has_relation_to_impl(db, other, inferable).negate(db);
|
let other_not_subtype_of_self = other.has_relation_to_impl(db, self).negate(db);
|
||||||
let other_not_subtype_of_self = other.has_relation_to_impl(db, self, inferable).negate(db);
|
|
||||||
self_not_subtype_of_other.intersect(db, other_not_subtype_of_self)
|
self_not_subtype_of_other.intersect(db, other_not_subtype_of_self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -238,14 +238,13 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
other.members(db).when_all(db, inferable, |other_member| {
|
other.members(db).when_all(db, |other_member| {
|
||||||
self.member_by_name(db, other_member.name).when_some_and(
|
self.member_by_name(db, other_member.name)
|
||||||
inferable,
|
.when_some_and(|our_member| match (our_member.kind, other_member.kind) {
|
||||||
|our_member| match (our_member.kind, other_member.kind) {
|
|
||||||
// Method members are always immutable;
|
// Method members are always immutable;
|
||||||
// they can never be subtypes of/assignable to mutable attribute members.
|
// they can never be subtypes of/assignable to mutable attribute members.
|
||||||
(ProtocolMemberKind::Method(_), ProtocolMemberKind::Other(_)) => {
|
(ProtocolMemberKind::Method(_), ProtocolMemberKind::Other(_)) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A property member can only be a subtype of an attribute member
|
// A property member can only be a subtype of an attribute member
|
||||||
|
|
@ -253,16 +252,15 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
//
|
//
|
||||||
// TODO: this should also consider the types of the members on both sides.
|
// TODO: this should also consider the types of the members on both sides.
|
||||||
(ProtocolMemberKind::Property(property), ProtocolMemberKind::Other(_)) => {
|
(ProtocolMemberKind::Property(property), ProtocolMemberKind::Other(_)) => {
|
||||||
ConstraintSet::from_bool(
|
ConstraintSet::from(
|
||||||
property.getter(db).is_some() && property.setter(db).is_some(),
|
property.getter(db).is_some() && property.setter(db).is_some(),
|
||||||
inferable,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A `@property` member can never be a subtype of a method member, as it is not necessarily
|
// A `@property` member can never be a subtype of a method member, as it is not necessarily
|
||||||
// accessible on the meta-type, whereas a method member must be.
|
// accessible on the meta-type, whereas a method member must be.
|
||||||
(ProtocolMemberKind::Property(_), ProtocolMemberKind::Method(_)) => {
|
(ProtocolMemberKind::Property(_), ProtocolMemberKind::Method(_)) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// But an attribute member *can* be a subtype of a method member,
|
// But an attribute member *can* be a subtype of a method member,
|
||||||
|
|
@ -270,9 +268,8 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
(
|
(
|
||||||
ProtocolMemberKind::Other(our_type),
|
ProtocolMemberKind::Other(our_type),
|
||||||
ProtocolMemberKind::Method(other_type),
|
ProtocolMemberKind::Method(other_type),
|
||||||
) => ConstraintSet::from_bool(
|
) => ConstraintSet::from(
|
||||||
our_member.qualifiers.contains(TypeQualifiers::CLASS_VAR),
|
our_member.qualifiers.contains(TypeQualifiers::CLASS_VAR),
|
||||||
inferable,
|
|
||||||
)
|
)
|
||||||
.and(db, || {
|
.and(db, || {
|
||||||
our_type.has_relation_to_impl(
|
our_type.has_relation_to_impl(
|
||||||
|
|
@ -327,9 +324,8 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
| ProtocolMemberKind::Method(_)
|
| ProtocolMemberKind::Method(_)
|
||||||
| ProtocolMemberKind::Other(_),
|
| ProtocolMemberKind::Other(_),
|
||||||
ProtocolMemberKind::Property(_),
|
ProtocolMemberKind::Property(_),
|
||||||
) => ConstraintSet::always(inferable),
|
) => ConstraintSet::from(true),
|
||||||
},
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -619,7 +615,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
// TODO: implement disjointness for property/method members as well as attribute members
|
// TODO: implement disjointness for property/method members as well as attribute members
|
||||||
ProtocolMemberKind::Property(_) | ProtocolMemberKind::Method(_) => {
|
ProtocolMemberKind::Property(_) | ProtocolMemberKind::Method(_) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
ProtocolMemberKind::Other(ty) => ty.is_disjoint_from_impl(
|
ProtocolMemberKind::Other(ty) => ty.is_disjoint_from_impl(
|
||||||
db,
|
db,
|
||||||
|
|
@ -655,7 +651,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||||
// complex interaction between `__new__`, `__init__` and metaclass `__call__`.
|
// complex interaction between `__new__`, `__init__` and metaclass `__call__`.
|
||||||
let attribute_type = if self.name == "__call__" {
|
let attribute_type = if self.name == "__call__" {
|
||||||
let Some(attribute_type) = other.try_upcast_to_callable(db) else {
|
let Some(attribute_type) = other.try_upcast_to_callable(db) else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
attribute_type
|
attribute_type
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -669,7 +665,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||||
)
|
)
|
||||||
.place
|
.place
|
||||||
else {
|
else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
attribute_type
|
attribute_type
|
||||||
};
|
};
|
||||||
|
|
@ -684,18 +680,15 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// TODO: consider the types of the attribute on `other` for property members
|
// TODO: consider the types of the attribute on `other` for property members
|
||||||
ProtocolMemberKind::Property(_) => ConstraintSet::from_bool(
|
ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
|
||||||
matches!(
|
other.member(db, self.name).place,
|
||||||
other.member(db, self.name).place,
|
Place::Defined(_, _, Definedness::AlwaysDefined)
|
||||||
Place::Defined(_, _, Definedness::AlwaysDefined)
|
)),
|
||||||
),
|
|
||||||
inferable,
|
|
||||||
),
|
|
||||||
ProtocolMemberKind::Other(member_type) => {
|
ProtocolMemberKind::Other(member_type) => {
|
||||||
let Place::Defined(attribute_type, _, Definedness::AlwaysDefined) =
|
let Place::Defined(attribute_type, _, Definedness::AlwaysDefined) =
|
||||||
other.member(db, self.name).place
|
other.member(db, self.name).place
|
||||||
else {
|
else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
member_type
|
member_type
|
||||||
.has_relation_to_impl(
|
.has_relation_to_impl(
|
||||||
|
|
|
||||||
|
|
@ -224,8 +224,8 @@ impl<'db> CallableSignature<'db> {
|
||||||
other,
|
other,
|
||||||
inferable,
|
inferable,
|
||||||
TypeRelation::Subtyping,
|
TypeRelation::Subtyping,
|
||||||
&HasRelationToVisitor::from_inferable(inferable),
|
&HasRelationToVisitor::default(),
|
||||||
&IsDisjointVisitor::from_inferable(inferable),
|
&IsDisjointVisitor::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,49 +274,43 @@ impl<'db> CallableSignature<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// `self` is possibly overloaded while `other` is definitely not overloaded.
|
// `self` is possibly overloaded while `other` is definitely not overloaded.
|
||||||
(_, [_]) => self_signatures
|
(_, [_]) => self_signatures.iter().when_any(db, |self_signature| {
|
||||||
.iter()
|
Self::has_relation_to_inner(
|
||||||
.when_any(db, inferable, |self_signature| {
|
db,
|
||||||
Self::has_relation_to_inner(
|
std::slice::from_ref(self_signature),
|
||||||
db,
|
other_signatures,
|
||||||
std::slice::from_ref(self_signature),
|
inferable,
|
||||||
other_signatures,
|
relation,
|
||||||
inferable,
|
relation_visitor,
|
||||||
relation,
|
disjointness_visitor,
|
||||||
relation_visitor,
|
)
|
||||||
disjointness_visitor,
|
}),
|
||||||
)
|
|
||||||
}),
|
|
||||||
|
|
||||||
// `self` is definitely not overloaded while `other` is possibly overloaded.
|
// `self` is definitely not overloaded while `other` is possibly overloaded.
|
||||||
([_], _) => other_signatures
|
([_], _) => other_signatures.iter().when_all(db, |other_signature| {
|
||||||
.iter()
|
Self::has_relation_to_inner(
|
||||||
.when_all(db, inferable, |other_signature| {
|
db,
|
||||||
Self::has_relation_to_inner(
|
self_signatures,
|
||||||
db,
|
std::slice::from_ref(other_signature),
|
||||||
self_signatures,
|
inferable,
|
||||||
std::slice::from_ref(other_signature),
|
relation,
|
||||||
inferable,
|
relation_visitor,
|
||||||
relation,
|
disjointness_visitor,
|
||||||
relation_visitor,
|
)
|
||||||
disjointness_visitor,
|
}),
|
||||||
)
|
|
||||||
}),
|
|
||||||
|
|
||||||
// `self` is definitely overloaded while `other` is possibly overloaded.
|
// `self` is definitely overloaded while `other` is possibly overloaded.
|
||||||
(_, _) => other_signatures
|
(_, _) => other_signatures.iter().when_all(db, |other_signature| {
|
||||||
.iter()
|
Self::has_relation_to_inner(
|
||||||
.when_all(db, inferable, |other_signature| {
|
db,
|
||||||
Self::has_relation_to_inner(
|
self_signatures,
|
||||||
db,
|
std::slice::from_ref(other_signature),
|
||||||
self_signatures,
|
inferable,
|
||||||
std::slice::from_ref(other_signature),
|
relation,
|
||||||
inferable,
|
relation_visitor,
|
||||||
relation,
|
disjointness_visitor,
|
||||||
relation_visitor,
|
)
|
||||||
disjointness_visitor,
|
}),
|
||||||
)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +332,7 @@ impl<'db> CallableSignature<'db> {
|
||||||
}
|
}
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
if self == other {
|
if self == other {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
self.is_subtype_of_impl(db, other, inferable)
|
self.is_subtype_of_impl(db, other, inferable)
|
||||||
.and(db, || other.is_subtype_of_impl(db, self, inferable))
|
.and(db, || other.is_subtype_of_impl(db, self, inferable))
|
||||||
|
|
@ -651,7 +645,7 @@ impl<'db> Signature<'db> {
|
||||||
inferable: InferableTypeVars<'db>,
|
inferable: InferableTypeVars<'db>,
|
||||||
visitor: &IsEquivalentVisitor<'db>,
|
visitor: &IsEquivalentVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::from(true);
|
||||||
let mut check_types = |self_type: Option<Type<'db>>, other_type: Option<Type<'db>>| {
|
let mut check_types = |self_type: Option<Type<'db>>, other_type: Option<Type<'db>>| {
|
||||||
let self_type = self_type.unwrap_or(Type::unknown());
|
let self_type = self_type.unwrap_or(Type::unknown());
|
||||||
let other_type = other_type.unwrap_or(Type::unknown());
|
let other_type = other_type.unwrap_or(Type::unknown());
|
||||||
|
|
@ -664,11 +658,11 @@ impl<'db> Signature<'db> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.parameters.is_gradual() != other.parameters.is_gradual() {
|
if self.parameters.is_gradual() != other.parameters.is_gradual() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.parameters.len() != other.parameters.len() {
|
if self.parameters.len() != other.parameters.len() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !check_types(self.return_ty, other.return_ty) {
|
if !check_types(self.return_ty, other.return_ty) {
|
||||||
|
|
@ -716,7 +710,7 @@ impl<'db> Signature<'db> {
|
||||||
|
|
||||||
(ParameterKind::KeywordVariadic { .. }, ParameterKind::KeywordVariadic { .. }) => {}
|
(ParameterKind::KeywordVariadic { .. }, ParameterKind::KeywordVariadic { .. }) => {}
|
||||||
|
|
||||||
_ => return ConstraintSet::never(inferable),
|
_ => return ConstraintSet::from(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !check_types(
|
if !check_types(
|
||||||
|
|
@ -740,9 +734,17 @@ impl<'db> Signature<'db> {
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
// If this callable is generic, then `inner` will add all of our typevars to the
|
// If either signature is generic, their typevars should also be considered inferable when
|
||||||
// `inferable` set, since we only need to find one specialization that causes the check to
|
// checking whether the signatures are equivalent, since we only need to find one
|
||||||
// succeed.
|
// specialization that causes the check to succeed.
|
||||||
|
let self_inferable = self.inferable_typevars(db);
|
||||||
|
let other_inferable = other.inferable_typevars(db);
|
||||||
|
let new_inferable = InferableTypeVars::none()
|
||||||
|
.merge(db, self_inferable)
|
||||||
|
.merge(db, other_inferable);
|
||||||
|
let inferable = inferable.merge(db, new_inferable);
|
||||||
|
|
||||||
|
// `inner` will create a constraint set that references these newly inferable typevars.
|
||||||
let when = self.has_relation_to_inner(
|
let when = self.has_relation_to_inner(
|
||||||
db,
|
db,
|
||||||
other,
|
other,
|
||||||
|
|
@ -756,7 +758,7 @@ impl<'db> Signature<'db> {
|
||||||
// we produce, we reduce it back down to the inferable set that the caller asked about.
|
// we produce, we reduce it back down to the inferable set that the caller asked about.
|
||||||
// If we introduced new inferable typevars, those will be existentially quantified away
|
// If we introduced new inferable typevars, those will be existentially quantified away
|
||||||
// before returning.
|
// before returning.
|
||||||
when.reduce_inferable(db, inferable)
|
when.reduce_inferable(db, new_inferable.iter(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_relation_to_inner(
|
fn has_relation_to_inner(
|
||||||
|
|
@ -829,12 +831,7 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The typevars in self and other should also be considered inferable when checking whether
|
let mut result = ConstraintSet::from(true);
|
||||||
// two signatures are equivalent.
|
|
||||||
let inferable = inferable.merge(db, self.inferable_typevars(db));
|
|
||||||
let inferable = inferable.merge(db, other.inferable_typevars(db));
|
|
||||||
|
|
||||||
let mut result = ConstraintSet::always(inferable);
|
|
||||||
let mut check_types = |type1: Option<Type<'db>>, type2: Option<Type<'db>>| {
|
let mut check_types = |type1: Option<Type<'db>>, type2: Option<Type<'db>>| {
|
||||||
let type1 = type1.unwrap_or(Type::unknown());
|
let type1 = type1.unwrap_or(Type::unknown());
|
||||||
let type2 = type2.unwrap_or(Type::unknown());
|
let type2 = type2.unwrap_or(Type::unknown());
|
||||||
|
|
@ -870,13 +867,13 @@ impl<'db> Signature<'db> {
|
||||||
.keyword_variadic()
|
.keyword_variadic()
|
||||||
.is_some_and(|(_, param)| param.annotated_type().is_some_and(|ty| ty.is_object()))
|
.is_some_and(|(_, param)| param.annotated_type().is_some_and(|ty| ty.is_object()))
|
||||||
{
|
{
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If either of the parameter lists is gradual (`...`), then it is assignable to and from
|
// If either of the parameter lists is gradual (`...`), then it is assignable to and from
|
||||||
// any other parameter list, but not a subtype or supertype of any other parameter list.
|
// any other parameter list, but not a subtype or supertype of any other parameter list.
|
||||||
if self.parameters.is_gradual() || other.parameters.is_gradual() {
|
if self.parameters.is_gradual() || other.parameters.is_gradual() {
|
||||||
return ConstraintSet::from_bool(relation.is_assignability(), inferable);
|
return ConstraintSet::from(relation.is_assignability());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut parameters = ParametersZip {
|
let mut parameters = ParametersZip {
|
||||||
|
|
@ -914,7 +911,7 @@ impl<'db> Signature<'db> {
|
||||||
// `other`, then the non-variadic parameters in `self` must have a default
|
// `other`, then the non-variadic parameters in `self` must have a default
|
||||||
// value.
|
// value.
|
||||||
if default_type.is_none() {
|
if default_type.is_none() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => {
|
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => {
|
||||||
|
|
@ -926,7 +923,7 @@ impl<'db> Signature<'db> {
|
||||||
EitherOrBoth::Right(_) => {
|
EitherOrBoth::Right(_) => {
|
||||||
// If there are more parameters in `other` than in `self`, then `self` is not a
|
// If there are more parameters in `other` than in `self`, then `self` is not a
|
||||||
// subtype of `other`.
|
// subtype of `other`.
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
EitherOrBoth::Both(self_parameter, other_parameter) => {
|
EitherOrBoth::Both(self_parameter, other_parameter) => {
|
||||||
|
|
@ -946,7 +943,7 @@ impl<'db> Signature<'db> {
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
if self_default.is_none() && other_default.is_some() {
|
if self_default.is_none() && other_default.is_some() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
if !check_types(
|
if !check_types(
|
||||||
other_parameter.annotated_type(),
|
other_parameter.annotated_type(),
|
||||||
|
|
@ -967,11 +964,11 @@ impl<'db> Signature<'db> {
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
if self_name != other_name {
|
if self_name != other_name {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
// The following checks are the same as positional-only parameters.
|
// The following checks are the same as positional-only parameters.
|
||||||
if self_default.is_none() && other_default.is_some() {
|
if self_default.is_none() && other_default.is_some() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
if !check_types(
|
if !check_types(
|
||||||
other_parameter.annotated_type(),
|
other_parameter.annotated_type(),
|
||||||
|
|
@ -1056,7 +1053,7 @@ impl<'db> Signature<'db> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => return ConstraintSet::never(inferable),
|
_ => return ConstraintSet::from(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1090,7 +1087,7 @@ impl<'db> Signature<'db> {
|
||||||
// previous loop. They cannot be matched against any parameter in `other` which
|
// previous loop. They cannot be matched against any parameter in `other` which
|
||||||
// only contains keyword-only and keyword-variadic parameters so the subtype
|
// only contains keyword-only and keyword-variadic parameters so the subtype
|
||||||
// relation is invalid.
|
// relation is invalid.
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
ParameterKind::Variadic { .. } => {}
|
ParameterKind::Variadic { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
@ -1117,7 +1114,7 @@ impl<'db> Signature<'db> {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if self_default.is_none() && other_default.is_some() {
|
if self_default.is_none() && other_default.is_some() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
if !check_types(
|
if !check_types(
|
||||||
other_parameter.annotated_type(),
|
other_parameter.annotated_type(),
|
||||||
|
|
@ -1138,14 +1135,14 @@ impl<'db> Signature<'db> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParameterKind::KeywordVariadic { .. } => {
|
ParameterKind::KeywordVariadic { .. } => {
|
||||||
let Some(self_keyword_variadic_type) = self_keyword_variadic else {
|
let Some(self_keyword_variadic_type) = self_keyword_variadic else {
|
||||||
// For a `self <: other` relationship, if `other` has a keyword variadic
|
// For a `self <: other` relationship, if `other` has a keyword variadic
|
||||||
// parameter, `self` must also have a keyword variadic parameter.
|
// parameter, `self` must also have a keyword variadic parameter.
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
if !check_types(other_parameter.annotated_type(), self_keyword_variadic_type) {
|
if !check_types(other_parameter.annotated_type(), self_keyword_variadic_type) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1153,7 +1150,7 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// This can only occur in case of a syntax error.
|
// This can only occur in case of a syntax error.
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1162,7 +1159,7 @@ impl<'db> Signature<'db> {
|
||||||
// optional otherwise the subtype relation is invalid.
|
// optional otherwise the subtype relation is invalid.
|
||||||
for (_, self_parameter) in self_keywords {
|
for (_, self_parameter) in self_keywords {
|
||||||
if self_parameter.default_type().is_none() {
|
if self_parameter.default_type().is_none() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,16 +143,13 @@ impl<'db> SubclassOfType<'db> {
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
match (self.subclass_of, other.subclass_of) {
|
match (self.subclass_of, other.subclass_of) {
|
||||||
(SubclassOfInner::Dynamic(_), SubclassOfInner::Dynamic(_)) => {
|
(SubclassOfInner::Dynamic(_), SubclassOfInner::Dynamic(_)) => {
|
||||||
ConstraintSet::from_bool(!relation.is_subtyping(), inferable)
|
ConstraintSet::from(!relation.is_subtyping())
|
||||||
}
|
}
|
||||||
(SubclassOfInner::Dynamic(_), SubclassOfInner::Class(other_class)) => {
|
(SubclassOfInner::Dynamic(_), SubclassOfInner::Class(other_class)) => {
|
||||||
ConstraintSet::from_bool(
|
ConstraintSet::from(other_class.is_object(db) || relation.is_assignability())
|
||||||
other_class.is_object(db) || relation.is_assignability(),
|
|
||||||
inferable,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
(SubclassOfInner::Class(_), SubclassOfInner::Dynamic(_)) => {
|
(SubclassOfInner::Class(_), SubclassOfInner::Dynamic(_)) => {
|
||||||
ConstraintSet::from_bool(relation.is_assignability(), inferable)
|
ConstraintSet::from(relation.is_assignability())
|
||||||
}
|
}
|
||||||
|
|
||||||
// For example, `type[bool]` describes all possible runtime subclasses of the class `bool`,
|
// For example, `type[bool]` describes all possible runtime subclasses of the class `bool`,
|
||||||
|
|
@ -177,18 +174,15 @@ impl<'db> SubclassOfType<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'db>,
|
_inferable: InferableTypeVars<'db>,
|
||||||
_visitor: &IsDisjointVisitor<'db>,
|
_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
match (self.subclass_of, other.subclass_of) {
|
match (self.subclass_of, other.subclass_of) {
|
||||||
(SubclassOfInner::Dynamic(_), _) | (_, SubclassOfInner::Dynamic(_)) => {
|
(SubclassOfInner::Dynamic(_), _) | (_, SubclassOfInner::Dynamic(_)) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
(SubclassOfInner::Class(self_class), SubclassOfInner::Class(other_class)) => {
|
(SubclassOfInner::Class(self_class), SubclassOfInner::Class(other_class)) => {
|
||||||
ConstraintSet::from_bool(
|
ConstraintSet::from(!self_class.could_coexist_in_mro_with(db, other_class))
|
||||||
!self_class.could_coexist_in_mro_with(db, other_class),
|
|
||||||
inferable,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -448,8 +448,8 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
match other {
|
match other {
|
||||||
Tuple::Fixed(other) => {
|
Tuple::Fixed(other) => {
|
||||||
ConstraintSet::from_bool(self.0.len() == other.0.len(), inferable).and(db, || {
|
ConstraintSet::from(self.0.len() == other.0.len()).and(db, || {
|
||||||
(self.0.iter().zip(&other.0)).when_all(db, inferable, |(self_ty, other_ty)| {
|
(self.0.iter().zip(&other.0)).when_all(db, |(self_ty, other_ty)| {
|
||||||
self_ty.has_relation_to_impl(
|
self_ty.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
*other_ty,
|
*other_ty,
|
||||||
|
|
@ -465,11 +465,11 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
Tuple::Variable(other) => {
|
Tuple::Variable(other) => {
|
||||||
// This tuple must have enough elements to match up with the other tuple's prefix
|
// This tuple must have enough elements to match up with the other tuple's prefix
|
||||||
// and suffix, and each of those elements must pairwise satisfy the relation.
|
// and suffix, and each of those elements must pairwise satisfy the relation.
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::from(true);
|
||||||
let mut self_iter = self.0.iter();
|
let mut self_iter = self.0.iter();
|
||||||
for other_ty in &other.prefix {
|
for other_ty in &other.prefix {
|
||||||
let Some(self_ty) = self_iter.next() else {
|
let Some(self_ty) = self_iter.next() else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
let element_constraints = self_ty.has_relation_to_impl(
|
let element_constraints = self_ty.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
|
|
@ -488,7 +488,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
for other_ty in other.suffix.iter().rev() {
|
for other_ty in other.suffix.iter().rev() {
|
||||||
let Some(self_ty) = self_iter.next_back() else {
|
let Some(self_ty) = self_iter.next_back() else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
let element_constraints = self_ty.has_relation_to_impl(
|
let element_constraints = self_ty.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
|
|
@ -509,7 +509,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
// In addition, any remaining elements in this tuple must satisfy the
|
// In addition, any remaining elements in this tuple must satisfy the
|
||||||
// variable-length portion of the other tuple.
|
// variable-length portion of the other tuple.
|
||||||
result.and(db, || {
|
result.and(db, || {
|
||||||
self_iter.when_all(db, inferable, |self_ty| {
|
self_iter.when_all(db, |self_ty| {
|
||||||
self_ty.has_relation_to_impl(
|
self_ty.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
other.variable,
|
other.variable,
|
||||||
|
|
@ -531,10 +531,10 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
inferable: InferableTypeVars<'db>,
|
inferable: InferableTypeVars<'db>,
|
||||||
visitor: &IsEquivalentVisitor<'db>,
|
visitor: &IsEquivalentVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
ConstraintSet::from_bool(self.0.len() == other.0.len(), inferable).and(db, || {
|
ConstraintSet::from(self.0.len() == other.0.len()).and(db, || {
|
||||||
(self.0.iter())
|
(self.0.iter())
|
||||||
.zip(&other.0)
|
.zip(&other.0)
|
||||||
.when_all(db, inferable, |(self_ty, other_ty)| {
|
.when_all(db, |(self_ty, other_ty)| {
|
||||||
self_ty.is_equivalent_to_impl(db, *other_ty, inferable, visitor)
|
self_ty.is_equivalent_to_impl(db, *other_ty, inferable, visitor)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -816,17 +816,17 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
// possible lengths. This means that `tuple[Any, ...]` can match any tuple of any
|
// possible lengths. This means that `tuple[Any, ...]` can match any tuple of any
|
||||||
// length.
|
// length.
|
||||||
if !relation.is_assignability() || !self.variable.is_dynamic() {
|
if !relation.is_assignability() || !self.variable.is_dynamic() {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In addition, the other tuple must have enough elements to match up with this
|
// In addition, the other tuple must have enough elements to match up with this
|
||||||
// tuple's prefix and suffix, and each of those elements must pairwise satisfy the
|
// tuple's prefix and suffix, and each of those elements must pairwise satisfy the
|
||||||
// relation.
|
// relation.
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::from(true);
|
||||||
let mut other_iter = other.elements().copied();
|
let mut other_iter = other.elements().copied();
|
||||||
for self_ty in self.prenormalized_prefix_elements(db, None) {
|
for self_ty in self.prenormalized_prefix_elements(db, None) {
|
||||||
let Some(other_ty) = other_iter.next() else {
|
let Some(other_ty) = other_iter.next() else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
let element_constraints = self_ty.has_relation_to_impl(
|
let element_constraints = self_ty.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
|
|
@ -846,7 +846,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
let suffix: Vec<_> = self.prenormalized_suffix_elements(db, None).collect();
|
let suffix: Vec<_> = self.prenormalized_suffix_elements(db, None).collect();
|
||||||
for self_ty in suffix.iter().rev() {
|
for self_ty in suffix.iter().rev() {
|
||||||
let Some(other_ty) = other_iter.next_back() else {
|
let Some(other_ty) = other_iter.next_back() else {
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
};
|
};
|
||||||
let element_constraints = self_ty.has_relation_to_impl(
|
let element_constraints = self_ty.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
|
|
@ -882,7 +882,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
// The overlapping parts of the prefixes and suffixes must satisfy the relation.
|
// The overlapping parts of the prefixes and suffixes must satisfy the relation.
|
||||||
// Any remaining parts must satisfy the relation with the other tuple's
|
// Any remaining parts must satisfy the relation with the other tuple's
|
||||||
// variable-length part.
|
// variable-length part.
|
||||||
let mut result = ConstraintSet::always(inferable);
|
let mut result = ConstraintSet::from(true);
|
||||||
let pairwise = (self.prenormalized_prefix_elements(db, self_prenormalize_variable))
|
let pairwise = (self.prenormalized_prefix_elements(db, self_prenormalize_variable))
|
||||||
.zip_longest(
|
.zip_longest(
|
||||||
other.prenormalized_prefix_elements(db, other_prenormalize_variable),
|
other.prenormalized_prefix_elements(db, other_prenormalize_variable),
|
||||||
|
|
@ -908,7 +908,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
EitherOrBoth::Right(_) => {
|
EitherOrBoth::Right(_) => {
|
||||||
// The rhs has a required element that the lhs is not guaranteed to
|
// The rhs has a required element that the lhs is not guaranteed to
|
||||||
// provide.
|
// provide.
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if result
|
if result
|
||||||
|
|
@ -947,7 +947,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
EitherOrBoth::Right(_) => {
|
EitherOrBoth::Right(_) => {
|
||||||
// The rhs has a required element that the lhs is not guaranteed to
|
// The rhs has a required element that the lhs is not guaranteed to
|
||||||
// provide.
|
// provide.
|
||||||
return ConstraintSet::never(inferable);
|
return ConstraintSet::from(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if result
|
if result
|
||||||
|
|
@ -985,24 +985,24 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
.and(db, || {
|
.and(db, || {
|
||||||
(self.prenormalized_prefix_elements(db, None))
|
(self.prenormalized_prefix_elements(db, None))
|
||||||
.zip_longest(other.prenormalized_prefix_elements(db, None))
|
.zip_longest(other.prenormalized_prefix_elements(db, None))
|
||||||
.when_all(db, inferable, |pair| match pair {
|
.when_all(db, |pair| match pair {
|
||||||
EitherOrBoth::Both(self_ty, other_ty) => {
|
EitherOrBoth::Both(self_ty, other_ty) => {
|
||||||
self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
|
self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
|
||||||
}
|
}
|
||||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.and(db, || {
|
.and(db, || {
|
||||||
(self.prenormalized_suffix_elements(db, None))
|
(self.prenormalized_suffix_elements(db, None))
|
||||||
.zip_longest(other.prenormalized_suffix_elements(db, None))
|
.zip_longest(other.prenormalized_suffix_elements(db, None))
|
||||||
.when_all(db, inferable, |pair| match pair {
|
.when_all(db, |pair| match pair {
|
||||||
EitherOrBoth::Both(self_ty, other_ty) => {
|
EitherOrBoth::Both(self_ty, other_ty) => {
|
||||||
self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
|
self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
|
||||||
}
|
}
|
||||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -1230,7 +1230,7 @@ impl<'db> Tuple<Type<'db>> {
|
||||||
self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor)
|
self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor)
|
||||||
}
|
}
|
||||||
(Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => {
|
(Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => {
|
||||||
ConstraintSet::never(inferable)
|
ConstraintSet::from(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1247,10 +1247,10 @@ impl<'db> Tuple<Type<'db>> {
|
||||||
let (self_min, self_max) = self.len().size_hint();
|
let (self_min, self_max) = self.len().size_hint();
|
||||||
let (other_min, other_max) = other.len().size_hint();
|
let (other_min, other_max) = other.len().size_hint();
|
||||||
if self_max.is_some_and(|max| max < other_min) {
|
if self_max.is_some_and(|max| max < other_min) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
if other_max.is_some_and(|max| max < self_min) {
|
if other_max.is_some_and(|max| max < self_min) {
|
||||||
return ConstraintSet::always(inferable);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any of the required elements are pairwise disjoint, the tuples are disjoint as well.
|
// If any of the required elements are pairwise disjoint, the tuples are disjoint as well.
|
||||||
|
|
@ -1266,7 +1266,7 @@ impl<'db> Tuple<Type<'db>> {
|
||||||
where
|
where
|
||||||
'db: 's,
|
'db: 's,
|
||||||
{
|
{
|
||||||
(a.into_iter().zip(b)).when_any(db, inferable, |(self_element, other_element)| {
|
(a.into_iter().zip(b)).when_any(db, |(self_element, other_element)| {
|
||||||
self_element.is_disjoint_from_impl(
|
self_element.is_disjoint_from_impl(
|
||||||
db,
|
db,
|
||||||
*other_element,
|
*other_element,
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,6 @@ class ConstraintSet:
|
||||||
def never() -> Self:
|
def never() -> Self:
|
||||||
"""Returns a constraint set that is never satisfied"""
|
"""Returns a constraint set that is never satisfied"""
|
||||||
|
|
||||||
def with_inferable(self, *inferable: Any) -> Self:
|
|
||||||
"""
|
|
||||||
Returns a copy of this constraint set with some typevars marked as being
|
|
||||||
in inferable position.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def implies_subtype_of(self, ty: Any, of: Any) -> Self:
|
def implies_subtype_of(self, ty: Any, of: Any) -> Self:
|
||||||
"""
|
"""
|
||||||
Returns a constraint set that is satisfied when `ty` is a `subtype`_ of
|
Returns a constraint set that is satisfied when `ty` is a `subtype`_ of
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue