mirror of https://github.com/astral-sh/ruff
[ty] Move constraint set mdtest functions into `ConstraintSet` class (#21108)
We have several functions in `ty_extensions` for testing our constraint
set implementation. This PR refactors those functions so that they are
all methods of the `ConstraintSet` class, rather than being standalone
top-level functions. 🎩 to @sharkdp for pointing out that
`KnownBoundMethod` gives us what we need to implement that!
This commit is contained in:
parent
7b959ef44b
commit
4d2ee41e24
|
|
@ -34,7 +34,7 @@ upper bound.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Any, final, Never, Sequence
|
from typing import Any, final, Never, Sequence
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -45,7 +45,7 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(range_constraint(Sub, T, Super))
|
reveal_type(ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
Every type is a supertype of `Never`, so a lower bound of `Never` is the same as having no lower
|
Every type is a supertype of `Never`, so a lower bound of `Never` is the same as having no lower
|
||||||
|
|
@ -54,7 +54,7 @@ bound.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(Never, T, Base))
|
reveal_type(ConstraintSet.range(Never, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly, every type is a subtype of `object`, so an upper bound of `object` is the same as having
|
Similarly, every type is a subtype of `object`, so an upper bound of `object` is the same as having
|
||||||
|
|
@ -63,7 +63,7 @@ no upper bound.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_)]
|
||||||
reveal_type(range_constraint(Base, T, object))
|
reveal_type(ConstraintSet.range(Base, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
And a range constraint with _both_ a lower bound of `Never` and an upper bound of `object` does not
|
And a range constraint with _both_ a lower bound of `Never` and an upper bound of `object` does not
|
||||||
|
|
@ -72,7 +72,7 @@ constrain the typevar at all.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(range_constraint(Never, T, object))
|
reveal_type(ConstraintSet.range(Never, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
If the lower bound and upper bounds are "inverted" (the upper bound is a subtype of the lower bound)
|
If the lower bound and upper bounds are "inverted" (the upper bound is a subtype of the lower bound)
|
||||||
|
|
@ -81,9 +81,9 @@ or incomparable, then there is no type that can satisfy the constraint.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(range_constraint(Super, T, Sub))
|
reveal_type(ConstraintSet.range(Super, T, Sub))
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(range_constraint(Base, T, Unrelated))
|
reveal_type(ConstraintSet.range(Base, T, Unrelated))
|
||||||
```
|
```
|
||||||
|
|
||||||
The lower and upper bound can be the same type, in which case the typevar can only be specialized to
|
The lower and upper bound can be the same type, in which case the typevar can only be specialized to
|
||||||
|
|
@ -92,7 +92,7 @@ that specific type.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@_ = Base)]
|
# revealed: ty_extensions.ConstraintSet[(T@_ = Base)]
|
||||||
reveal_type(range_constraint(Base, T, Base))
|
reveal_type(ConstraintSet.range(Base, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
Constraints can only refer to fully static types, so the lower and upper bounds are transformed into
|
Constraints can only refer to fully static types, so the lower and upper bounds are transformed into
|
||||||
|
|
@ -101,14 +101,14 @@ their bottom and top materializations, respectively.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_)]
|
||||||
reveal_type(range_constraint(Base, T, Any))
|
reveal_type(ConstraintSet.range(Base, T, Any))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sequence[Base] ≤ T@_ ≤ Sequence[object])]
|
# revealed: ty_extensions.ConstraintSet[(Sequence[Base] ≤ T@_ ≤ Sequence[object])]
|
||||||
reveal_type(range_constraint(Sequence[Base], T, Sequence[Any]))
|
reveal_type(ConstraintSet.range(Sequence[Base], T, Sequence[Any]))
|
||||||
|
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(Any, T, Base))
|
reveal_type(ConstraintSet.range(Any, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sequence[Never] ≤ T@_ ≤ Sequence[Base])]
|
# revealed: ty_extensions.ConstraintSet[(Sequence[Never] ≤ T@_ ≤ Sequence[Base])]
|
||||||
reveal_type(range_constraint(Sequence[Any], T, Sequence[Base]))
|
reveal_type(ConstraintSet.range(Sequence[Any], T, Sequence[Base]))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Negated range
|
### Negated range
|
||||||
|
|
@ -119,7 +119,7 @@ strict subtype of the lower bound, a strict supertype of the upper bound, or inc
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Any, final, Never, Sequence
|
from typing import Any, final, Never, Sequence
|
||||||
from ty_extensions import negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -130,7 +130,7 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Super))
|
reveal_type(~ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
Every type is a supertype of `Never`, so a lower bound of `Never` is the same as having no lower
|
Every type is a supertype of `Never`, so a lower bound of `Never` is the same as having no lower
|
||||||
|
|
@ -139,7 +139,7 @@ bound.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(Never, T, Base))
|
reveal_type(~ConstraintSet.range(Never, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly, every type is a subtype of `object`, so an upper bound of `object` is the same as having
|
Similarly, every type is a subtype of `object`, so an upper bound of `object` is the same as having
|
||||||
|
|
@ -148,7 +148,7 @@ no upper bound.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Base ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[¬(Base ≤ T@_)]
|
||||||
reveal_type(negated_range_constraint(Base, T, object))
|
reveal_type(~ConstraintSet.range(Base, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
And a negated range constraint with _both_ a lower bound of `Never` and an upper bound of `object`
|
And a negated range constraint with _both_ a lower bound of `Never` and an upper bound of `object`
|
||||||
|
|
@ -157,7 +157,7 @@ cannot be satisfied at all.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(negated_range_constraint(Never, T, object))
|
reveal_type(~ConstraintSet.range(Never, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
If the lower bound and upper bounds are "inverted" (the upper bound is a subtype of the lower bound)
|
If the lower bound and upper bounds are "inverted" (the upper bound is a subtype of the lower bound)
|
||||||
|
|
@ -166,9 +166,9 @@ or incomparable, then the negated range constraint can always be satisfied.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(negated_range_constraint(Super, T, Sub))
|
reveal_type(~ConstraintSet.range(Super, T, Sub))
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(negated_range_constraint(Base, T, Unrelated))
|
reveal_type(~ConstraintSet.range(Base, T, Unrelated))
|
||||||
```
|
```
|
||||||
|
|
||||||
The lower and upper bound can be the same type, in which case the typevar can be specialized to any
|
The lower and upper bound can be the same type, in which case the typevar can be specialized to any
|
||||||
|
|
@ -177,7 +177,7 @@ type other than that specific type.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@_ ≠ Base)]
|
# revealed: ty_extensions.ConstraintSet[(T@_ ≠ Base)]
|
||||||
reveal_type(negated_range_constraint(Base, T, Base))
|
reveal_type(~ConstraintSet.range(Base, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
Constraints can only refer to fully static types, so the lower and upper bounds are transformed into
|
Constraints can only refer to fully static types, so the lower and upper bounds are transformed into
|
||||||
|
|
@ -186,14 +186,14 @@ their bottom and top materializations, respectively.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Base ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[¬(Base ≤ T@_)]
|
||||||
reveal_type(negated_range_constraint(Base, T, Any))
|
reveal_type(~ConstraintSet.range(Base, T, Any))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sequence[Base] ≤ T@_ ≤ Sequence[object])]
|
# revealed: ty_extensions.ConstraintSet[¬(Sequence[Base] ≤ T@_ ≤ Sequence[object])]
|
||||||
reveal_type(negated_range_constraint(Sequence[Base], T, Sequence[Any]))
|
reveal_type(~ConstraintSet.range(Sequence[Base], T, Sequence[Any]))
|
||||||
|
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(Any, T, Base))
|
reveal_type(~ConstraintSet.range(Any, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sequence[Never] ≤ T@_ ≤ Sequence[Base])]
|
# revealed: ty_extensions.ConstraintSet[¬(Sequence[Never] ≤ T@_ ≤ Sequence[Base])]
|
||||||
reveal_type(negated_range_constraint(Sequence[Any], T, Sequence[Base]))
|
reveal_type(~ConstraintSet.range(Sequence[Any], T, Sequence[Base]))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Intersection
|
## Intersection
|
||||||
|
|
@ -204,7 +204,7 @@ cases, we can simplify the result of an intersection.
|
||||||
### Different typevars
|
### Different typevars
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import range_constraint, negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -216,9 +216,9 @@ We cannot simplify the intersection of constraints that refer to different typev
|
||||||
```py
|
```py
|
||||||
def _[T, U]() -> None:
|
def _[T, U]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[((Sub ≤ T@_ ≤ Base) ∧ (Sub ≤ U@_ ≤ Base))]
|
# revealed: ty_extensions.ConstraintSet[((Sub ≤ T@_ ≤ Base) ∧ (Sub ≤ U@_ ≤ Base))]
|
||||||
reveal_type(range_constraint(Sub, T, Base) & range_constraint(Sub, U, Base))
|
reveal_type(ConstraintSet.range(Sub, T, Base) & ConstraintSet.range(Sub, U, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(Sub ≤ T@_ ≤ Base) ∧ ¬(Sub ≤ U@_ ≤ Base))]
|
# revealed: ty_extensions.ConstraintSet[(¬(Sub ≤ T@_ ≤ Base) ∧ ¬(Sub ≤ U@_ ≤ Base))]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) & negated_range_constraint(Sub, U, Base))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) & ~ConstraintSet.range(Sub, U, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Intersection of two ranges
|
### Intersection of two ranges
|
||||||
|
|
@ -227,7 +227,7 @@ The intersection of two ranges is where the ranges "overlap".
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final
|
from typing import final
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -239,13 +239,13 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(SubSub, T, Base) & range_constraint(Sub, T, Super))
|
reveal_type(ConstraintSet.range(SubSub, T, Base) & ConstraintSet.range(Sub, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(SubSub, T, Super) & range_constraint(Sub, T, Base))
|
reveal_type(ConstraintSet.range(SubSub, T, Super) & ConstraintSet.range(Sub, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@_ = Base)]
|
# revealed: ty_extensions.ConstraintSet[(T@_ = Base)]
|
||||||
reveal_type(range_constraint(Sub, T, Base) & range_constraint(Base, T, Super))
|
reveal_type(ConstraintSet.range(Sub, T, Base) & ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(range_constraint(Sub, T, Super) & range_constraint(Sub, T, Super))
|
reveal_type(ConstraintSet.range(Sub, T, Super) & ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
If they don't overlap, the intersection is empty.
|
If they don't overlap, the intersection is empty.
|
||||||
|
|
@ -253,9 +253,9 @@ If they don't overlap, the intersection is empty.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) & range_constraint(Base, T, Super))
|
reveal_type(ConstraintSet.range(SubSub, T, Sub) & ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) & range_constraint(Unrelated, T, object))
|
reveal_type(ConstraintSet.range(SubSub, T, Sub) & ConstraintSet.range(Unrelated, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Intersection of a range and a negated range
|
### Intersection of a range and a negated range
|
||||||
|
|
@ -266,7 +266,7 @@ the intersection as removing the hole from the range constraint.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final, Never
|
from typing import final, Never
|
||||||
from ty_extensions import range_constraint, negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -282,9 +282,9 @@ If the negative range completely contains the positive range, then the intersect
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(range_constraint(Sub, T, Base) & negated_range_constraint(SubSub, T, Super))
|
reveal_type(ConstraintSet.range(Sub, T, Base) & ~ConstraintSet.range(SubSub, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(range_constraint(Sub, T, Base) & negated_range_constraint(Sub, T, Base))
|
reveal_type(ConstraintSet.range(Sub, T, Base) & ~ConstraintSet.range(Sub, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
If the negative range is disjoint from the positive range, the negative range doesn't remove
|
If the negative range is disjoint from the positive range, the negative range doesn't remove
|
||||||
|
|
@ -293,11 +293,11 @@ anything; the intersection is the positive range.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(Sub, T, Base) & negated_range_constraint(Never, T, Unrelated))
|
reveal_type(ConstraintSet.range(Sub, T, Base) & ~ConstraintSet.range(Never, T, Unrelated))
|
||||||
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Sub)]
|
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Sub)]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) & negated_range_constraint(Base, T, Super))
|
reveal_type(ConstraintSet.range(SubSub, T, Sub) & ~ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super)]
|
||||||
reveal_type(range_constraint(Base, T, Super) & negated_range_constraint(SubSub, T, Sub))
|
reveal_type(ConstraintSet.range(Base, T, Super) & ~ConstraintSet.range(SubSub, T, Sub))
|
||||||
```
|
```
|
||||||
|
|
||||||
Otherwise we clip the negative constraint to the mininum range that overlaps with the positive
|
Otherwise we clip the negative constraint to the mininum range that overlaps with the positive
|
||||||
|
|
@ -306,9 +306,9 @@ range.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[((SubSub ≤ T@_ ≤ Base) ∧ ¬(Sub ≤ T@_ ≤ Base))]
|
# revealed: ty_extensions.ConstraintSet[((SubSub ≤ T@_ ≤ Base) ∧ ¬(Sub ≤ T@_ ≤ Base))]
|
||||||
reveal_type(range_constraint(SubSub, T, Base) & negated_range_constraint(Sub, T, Super))
|
reveal_type(ConstraintSet.range(SubSub, T, Base) & ~ConstraintSet.range(Sub, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[((SubSub ≤ T@_ ≤ Super) ∧ ¬(Sub ≤ T@_ ≤ Base))]
|
# revealed: ty_extensions.ConstraintSet[((SubSub ≤ T@_ ≤ Super) ∧ ¬(Sub ≤ T@_ ≤ Base))]
|
||||||
reveal_type(range_constraint(SubSub, T, Super) & negated_range_constraint(Sub, T, Base))
|
reveal_type(ConstraintSet.range(SubSub, T, Super) & ~ConstraintSet.range(Sub, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Intersection of two negated ranges
|
### Intersection of two negated ranges
|
||||||
|
|
@ -318,7 +318,7 @@ smaller constraint. For negated ranges, the smaller constraint is the one with t
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final
|
from typing import final
|
||||||
from ty_extensions import negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -330,9 +330,9 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(SubSub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[¬(SubSub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Super) & negated_range_constraint(Sub, T, Base))
|
reveal_type(~ConstraintSet.range(SubSub, T, Super) & ~ConstraintSet.range(Sub, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Super) & negated_range_constraint(Sub, T, Super))
|
reveal_type(~ConstraintSet.range(Sub, T, Super) & ~ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
Otherwise, the union cannot be simplified.
|
Otherwise, the union cannot be simplified.
|
||||||
|
|
@ -340,11 +340,11 @@ Otherwise, the union cannot be simplified.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(Base ≤ T@_ ≤ Super) ∧ ¬(Sub ≤ T@_ ≤ Base))]
|
# revealed: ty_extensions.ConstraintSet[(¬(Base ≤ T@_ ≤ Super) ∧ ¬(Sub ≤ T@_ ≤ Base))]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) & negated_range_constraint(Base, T, Super))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) & ~ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(Base ≤ T@_ ≤ Super) ∧ ¬(SubSub ≤ T@_ ≤ Sub))]
|
# revealed: ty_extensions.ConstraintSet[(¬(Base ≤ T@_ ≤ Super) ∧ ¬(SubSub ≤ T@_ ≤ Sub))]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) & negated_range_constraint(Base, T, Super))
|
reveal_type(~ConstraintSet.range(SubSub, T, Sub) & ~ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(SubSub ≤ T@_ ≤ Sub) ∧ ¬(Unrelated ≤ T@_))]
|
# revealed: ty_extensions.ConstraintSet[(¬(SubSub ≤ T@_ ≤ Sub) ∧ ¬(Unrelated ≤ T@_))]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) & negated_range_constraint(Unrelated, T, object))
|
reveal_type(~ConstraintSet.range(SubSub, T, Sub) & ~ConstraintSet.range(Unrelated, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
In particular, the following does not simplify, even though it seems like it could simplify to
|
In particular, the following does not simplify, even though it seems like it could simplify to
|
||||||
|
|
@ -361,7 +361,7 @@ that type _is_ in `SubSub ≤ T ≤ Super`, it is not correct to simplify the un
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(Sub ≤ T@_ ≤ Super) ∧ ¬(SubSub ≤ T@_ ≤ Base))]
|
# revealed: ty_extensions.ConstraintSet[(¬(Sub ≤ T@_ ≤ Super) ∧ ¬(SubSub ≤ T@_ ≤ Base))]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Base) & negated_range_constraint(Sub, T, Super))
|
reveal_type(~ConstraintSet.range(SubSub, T, Base) & ~ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Union
|
## Union
|
||||||
|
|
@ -372,7 +372,7 @@ can simplify the result of an union.
|
||||||
### Different typevars
|
### Different typevars
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import range_constraint, negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -384,9 +384,9 @@ We cannot simplify the union of constraints that refer to different typevars.
|
||||||
```py
|
```py
|
||||||
def _[T, U]() -> None:
|
def _[T, U]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base) ∨ (Sub ≤ U@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base) ∨ (Sub ≤ U@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(Sub, T, Base) | range_constraint(Sub, U, Base))
|
reveal_type(ConstraintSet.range(Sub, T, Base) | ConstraintSet.range(Sub, U, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base) ∨ ¬(Sub ≤ U@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base) ∨ ¬(Sub ≤ U@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) | negated_range_constraint(Sub, U, Base))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) | ~ConstraintSet.range(Sub, U, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Union of two ranges
|
### Union of two ranges
|
||||||
|
|
@ -396,7 +396,7 @@ bounds.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final
|
from typing import final
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -408,9 +408,9 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(range_constraint(SubSub, T, Super) | range_constraint(Sub, T, Base))
|
reveal_type(ConstraintSet.range(SubSub, T, Super) | ConstraintSet.range(Sub, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(range_constraint(Sub, T, Super) | range_constraint(Sub, T, Super))
|
reveal_type(ConstraintSet.range(Sub, T, Super) | ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
Otherwise, the union cannot be simplified.
|
Otherwise, the union cannot be simplified.
|
||||||
|
|
@ -418,11 +418,11 @@ Otherwise, the union cannot be simplified.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super) ∨ (Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super) ∨ (Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(Sub, T, Base) | range_constraint(Base, T, Super))
|
reveal_type(ConstraintSet.range(Sub, T, Base) | ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super) ∨ (SubSub ≤ T@_ ≤ Sub)]
|
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super) ∨ (SubSub ≤ T@_ ≤ Sub)]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) | range_constraint(Base, T, Super))
|
reveal_type(ConstraintSet.range(SubSub, T, Sub) | ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Sub) ∨ (Unrelated ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Sub) ∨ (Unrelated ≤ T@_)]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) | range_constraint(Unrelated, T, object))
|
reveal_type(ConstraintSet.range(SubSub, T, Sub) | ConstraintSet.range(Unrelated, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
In particular, the following does not simplify, even though it seems like it could simplify to
|
In particular, the following does not simplify, even though it seems like it could simplify to
|
||||||
|
|
@ -438,7 +438,7 @@ not include `Sub`. That means it should not be in the union. Since that type _is
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super) ∨ (SubSub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Super) ∨ (SubSub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(range_constraint(SubSub, T, Base) | range_constraint(Sub, T, Super))
|
reveal_type(ConstraintSet.range(SubSub, T, Base) | ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Union of a range and a negated range
|
### Union of a range and a negated range
|
||||||
|
|
@ -449,7 +449,7 @@ the union as filling part of the hole with the types from the range constraint.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final, Never
|
from typing import final, Never
|
||||||
from ty_extensions import range_constraint, negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -465,9 +465,9 @@ If the positive range completely contains the negative range, then the union is
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) | range_constraint(SubSub, T, Super))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) | ConstraintSet.range(SubSub, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) | range_constraint(Sub, T, Base))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) | ConstraintSet.range(Sub, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
If the negative range is disjoint from the positive range, the positive range doesn't add anything;
|
If the negative range is disjoint from the positive range, the positive range doesn't add anything;
|
||||||
|
|
@ -476,11 +476,11 @@ the union is the negative range.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) | range_constraint(Never, T, Unrelated))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) | ConstraintSet.range(Never, T, Unrelated))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(SubSub ≤ T@_ ≤ Sub)]
|
# revealed: ty_extensions.ConstraintSet[¬(SubSub ≤ T@_ ≤ Sub)]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) | range_constraint(Base, T, Super))
|
reveal_type(~ConstraintSet.range(SubSub, T, Sub) | ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Base ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[¬(Base ≤ T@_ ≤ Super)]
|
||||||
reveal_type(negated_range_constraint(Base, T, Super) | range_constraint(SubSub, T, Sub))
|
reveal_type(~ConstraintSet.range(Base, T, Super) | ConstraintSet.range(SubSub, T, Sub))
|
||||||
```
|
```
|
||||||
|
|
||||||
Otherwise we clip the positive constraint to the mininum range that overlaps with the negative
|
Otherwise we clip the positive constraint to the mininum range that overlaps with the negative
|
||||||
|
|
@ -489,9 +489,9 @@ range.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base) ∨ ¬(SubSub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base) ∨ ¬(SubSub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Base) | range_constraint(Sub, T, Super))
|
reveal_type(~ConstraintSet.range(SubSub, T, Base) | ConstraintSet.range(Sub, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base) ∨ ¬(SubSub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@_ ≤ Base) ∨ ¬(SubSub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Super) | range_constraint(Sub, T, Base))
|
reveal_type(~ConstraintSet.range(SubSub, T, Super) | ConstraintSet.range(Sub, T, Base))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Union of two negated ranges
|
### Union of two negated ranges
|
||||||
|
|
@ -500,7 +500,7 @@ The union of two negated ranges has a hole where the ranges "overlap".
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final
|
from typing import final
|
||||||
from ty_extensions import negated_range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -512,13 +512,13 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Base) | negated_range_constraint(Sub, T, Super))
|
reveal_type(~ConstraintSet.range(SubSub, T, Base) | ~ConstraintSet.range(Sub, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Super) | negated_range_constraint(Sub, T, Base))
|
reveal_type(~ConstraintSet.range(SubSub, T, Super) | ~ConstraintSet.range(Sub, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@_ ≠ Base)]
|
# revealed: ty_extensions.ConstraintSet[(T@_ ≠ Base)]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) | negated_range_constraint(Base, T, Super))
|
reveal_type(~ConstraintSet.range(Sub, T, Base) | ~ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Super)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Super)]
|
||||||
reveal_type(negated_range_constraint(Sub, T, Super) | negated_range_constraint(Sub, T, Super))
|
reveal_type(~ConstraintSet.range(Sub, T, Super) | ~ConstraintSet.range(Sub, T, Super))
|
||||||
```
|
```
|
||||||
|
|
||||||
If the holes don't overlap, the union is always satisfied.
|
If the holes don't overlap, the union is always satisfied.
|
||||||
|
|
@ -526,9 +526,9 @@ If the holes don't overlap, the union is always satisfied.
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) | negated_range_constraint(Base, T, Super))
|
reveal_type(~ConstraintSet.range(SubSub, T, Sub) | ~ConstraintSet.range(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) | negated_range_constraint(Unrelated, T, object))
|
reveal_type(~ConstraintSet.range(SubSub, T, Sub) | ~ConstraintSet.range(Unrelated, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Negation
|
## Negation
|
||||||
|
|
@ -537,7 +537,7 @@ def _[T]() -> None:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Never
|
from typing import Never
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
|
@ -545,20 +545,20 @@ class Sub(Base): ...
|
||||||
|
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_ ≤ Base)]
|
||||||
reveal_type(~range_constraint(Sub, T, Base))
|
reveal_type(~ConstraintSet.range(Sub, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base)]
|
||||||
reveal_type(~range_constraint(Never, T, Base))
|
reveal_type(~ConstraintSet.range(Never, T, Base))
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[¬(Sub ≤ T@_)]
|
||||||
reveal_type(~range_constraint(Sub, T, object))
|
reveal_type(~ConstraintSet.range(Sub, T, object))
|
||||||
# revealed: ty_extensions.ConstraintSet[never]
|
# revealed: ty_extensions.ConstraintSet[never]
|
||||||
reveal_type(~range_constraint(Never, T, object))
|
reveal_type(~ConstraintSet.range(Never, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
The union of a range constraint and its negation should always be satisfiable.
|
The union of a range constraint and its negation should always be satisfiable.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _[T]() -> None:
|
def _[T]() -> None:
|
||||||
constraint = range_constraint(Sub, T, Base)
|
constraint = ConstraintSet.range(Sub, T, Base)
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(constraint | ~constraint)
|
reveal_type(constraint | ~constraint)
|
||||||
```
|
```
|
||||||
|
|
@ -567,7 +567,7 @@ def _[T]() -> None:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import final, Never
|
from typing import final, Never
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
class Base: ...
|
class Base: ...
|
||||||
|
|
||||||
|
|
@ -576,20 +576,20 @@ class Unrelated: ...
|
||||||
|
|
||||||
def _[T, U]() -> None:
|
def _[T, U]() -> None:
|
||||||
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base) ∨ ¬(U@_ ≤ Base)]
|
# revealed: ty_extensions.ConstraintSet[¬(T@_ ≤ Base) ∨ ¬(U@_ ≤ Base)]
|
||||||
reveal_type(~(range_constraint(Never, T, Base) & range_constraint(Never, U, Base)))
|
reveal_type(~(ConstraintSet.range(Never, T, Base) & ConstraintSet.range(Never, U, Base)))
|
||||||
```
|
```
|
||||||
|
|
||||||
The union of a constraint and its negation should always be satisfiable.
|
The union of a constraint and its negation should always be satisfiable.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _[T, U]() -> None:
|
def _[T, U]() -> None:
|
||||||
c1 = range_constraint(Never, T, Base) & range_constraint(Never, U, Base)
|
c1 = ConstraintSet.range(Never, T, Base) & ConstraintSet.range(Never, U, Base)
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(c1 | ~c1)
|
reveal_type(c1 | ~c1)
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(~c1 | c1)
|
reveal_type(~c1 | c1)
|
||||||
|
|
||||||
c2 = range_constraint(Unrelated, T, object) & range_constraint(Unrelated, U, object)
|
c2 = ConstraintSet.range(Unrelated, T, object) & ConstraintSet.range(Unrelated, U, object)
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(c2 | ~c2)
|
reveal_type(c2 | ~c2)
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
|
|
@ -614,19 +614,19 @@ since we always hide `Never` lower bounds and `object` upper bounds.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Never
|
from typing import Never
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
def f[S, T]():
|
def f[S, T]():
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
||||||
reveal_type(range_constraint(Never, S, T))
|
reveal_type(ConstraintSet.range(Never, S, T))
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
||||||
reveal_type(range_constraint(S, T, object))
|
reveal_type(ConstraintSet.range(S, T, object))
|
||||||
|
|
||||||
def f[T, S]():
|
def f[T, S]():
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
||||||
reveal_type(range_constraint(Never, S, T))
|
reveal_type(ConstraintSet.range(Never, S, T))
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f ≤ T@f)]
|
||||||
reveal_type(range_constraint(S, T, object))
|
reveal_type(ConstraintSet.range(S, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
Equivalence constraints are similar; internally we arbitrarily choose the "earlier" typevar to be
|
Equivalence constraints are similar; internally we arbitrarily choose the "earlier" typevar to be
|
||||||
|
|
@ -635,15 +635,15 @@ the constraint, and the other the bound. But we display the result the same way
|
||||||
```py
|
```py
|
||||||
def f[S, T]():
|
def f[S, T]():
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
||||||
reveal_type(range_constraint(T, S, T))
|
reveal_type(ConstraintSet.range(T, S, T))
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
||||||
reveal_type(range_constraint(S, T, S))
|
reveal_type(ConstraintSet.range(S, T, S))
|
||||||
|
|
||||||
def f[T, S]():
|
def f[T, S]():
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
||||||
reveal_type(range_constraint(T, S, T))
|
reveal_type(ConstraintSet.range(T, S, T))
|
||||||
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
# revealed: ty_extensions.ConstraintSet[(S@f = T@f)]
|
||||||
reveal_type(range_constraint(S, T, S))
|
reveal_type(ConstraintSet.range(S, T, S))
|
||||||
```
|
```
|
||||||
|
|
||||||
But in the case of `S ≤ T ≤ U`, we end up with an ambiguity. Depending on the typevar ordering, that
|
But in the case of `S ≤ T ≤ U`, we end up with an ambiguity. Depending on the typevar ordering, that
|
||||||
|
|
@ -654,7 +654,7 @@ def f[S, T, U]():
|
||||||
# Could be either of:
|
# Could be either of:
|
||||||
# ty_extensions.ConstraintSet[(S@f ≤ T@f ≤ U@f)]
|
# ty_extensions.ConstraintSet[(S@f ≤ T@f ≤ U@f)]
|
||||||
# ty_extensions.ConstraintSet[(S@f ≤ T@f) ∧ (T@f ≤ U@f)]
|
# ty_extensions.ConstraintSet[(S@f ≤ T@f) ∧ (T@f ≤ U@f)]
|
||||||
# reveal_type(range_constraint(S, T, U))
|
# reveal_type(ConstraintSet.range(S, T, U))
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -668,13 +668,13 @@ This section contains several examples that show that we simplify the DNF formul
|
||||||
before displaying it.
|
before displaying it.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
def f[T, U]():
|
def f[T, U]():
|
||||||
t1 = range_constraint(str, T, str)
|
t1 = ConstraintSet.range(str, T, str)
|
||||||
t2 = range_constraint(bool, T, bool)
|
t2 = ConstraintSet.range(bool, T, bool)
|
||||||
u1 = range_constraint(str, U, str)
|
u1 = ConstraintSet.range(str, U, str)
|
||||||
u2 = range_constraint(bool, U, bool)
|
u2 = ConstraintSet.range(bool, U, bool)
|
||||||
|
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@f = bool) ∨ (T@f = str)]
|
# revealed: ty_extensions.ConstraintSet[(T@f = bool) ∨ (T@f = str)]
|
||||||
reveal_type(t1 | t2)
|
reveal_type(t1 | t2)
|
||||||
|
|
@ -692,8 +692,8 @@ from typing import Never
|
||||||
from ty_extensions import static_assert
|
from ty_extensions import static_assert
|
||||||
|
|
||||||
def f[T]():
|
def f[T]():
|
||||||
t_int = range_constraint(Never, T, int)
|
t_int = ConstraintSet.range(Never, T, int)
|
||||||
t_bool = range_constraint(Never, T, bool)
|
t_bool = ConstraintSet.range(Never, T, bool)
|
||||||
|
|
||||||
# `T ≤ bool` implies `T ≤ int`: if a type satisfies the former, it must always satisfy the
|
# `T ≤ bool` implies `T ≤ int`: if a type satisfies the former, it must always satisfy the
|
||||||
# latter. We can turn that into a constraint set, using the equivalence `p → q == ¬p ∨ q`:
|
# latter. We can turn that into a constraint set, using the equivalence `p → q == ¬p ∨ q`:
|
||||||
|
|
@ -707,7 +707,7 @@ def f[T]():
|
||||||
# "domain", which maps valid inputs to `true` and invalid inputs to `false`. This means that two
|
# "domain", which maps valid inputs to `true` and invalid inputs to `false`. This means that two
|
||||||
# constraint sets that are both always satisfied will not be identical if they have different
|
# constraint sets that are both always satisfied will not be identical if they have different
|
||||||
# domains!
|
# domains!
|
||||||
always = range_constraint(Never, T, object)
|
always = ConstraintSet.range(Never, T, object)
|
||||||
# revealed: ty_extensions.ConstraintSet[always]
|
# revealed: ty_extensions.ConstraintSet[always]
|
||||||
reveal_type(always)
|
reveal_type(always)
|
||||||
static_assert(always)
|
static_assert(always)
|
||||||
|
|
@ -721,11 +721,11 @@ intersections whose elements appear in different orders.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Never
|
from typing import Never
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
def f[T]():
|
def f[T]():
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@f ≤ int | str)]
|
# revealed: ty_extensions.ConstraintSet[(T@f ≤ int | str)]
|
||||||
reveal_type(range_constraint(Never, T, str | int))
|
reveal_type(ConstraintSet.range(Never, T, str | int))
|
||||||
# revealed: ty_extensions.ConstraintSet[(T@f ≤ int | str)]
|
# revealed: ty_extensions.ConstraintSet[(T@f ≤ int | str)]
|
||||||
reveal_type(range_constraint(Never, T, int | str))
|
reveal_type(ConstraintSet.range(Never, T, int | str))
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
python-version = "3.12"
|
python-version = "3.12"
|
||||||
```
|
```
|
||||||
|
|
||||||
This file tests the _constraint implication_ relationship between types, aka `is_subtype_of_given`,
|
This file tests the _constraint implication_ relationship between types, aka `implies_subtype_of`,
|
||||||
which tests whether one type is a [subtype][subtyping] of another _assuming that the constraints in
|
which tests whether one type is a [subtype][subtyping] of another _assuming that the constraints in
|
||||||
a particular constraint set hold_.
|
a particular constraint set hold_.
|
||||||
|
|
||||||
|
|
@ -16,14 +16,14 @@ fully static type that is not a typevar. It can _contain_ a typevar, though —
|
||||||
considered concrete.)
|
considered concrete.)
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import is_subtype_of, is_subtype_of_given, static_assert
|
from ty_extensions import ConstraintSet, is_subtype_of, static_assert
|
||||||
|
|
||||||
def equivalent_to_other_relationships[T]():
|
def equivalent_to_other_relationships[T]():
|
||||||
static_assert(is_subtype_of(bool, int))
|
static_assert(is_subtype_of(bool, int))
|
||||||
static_assert(is_subtype_of_given(True, bool, int))
|
static_assert(ConstraintSet.always().implies_subtype_of(bool, int))
|
||||||
|
|
||||||
static_assert(not is_subtype_of(bool, str))
|
static_assert(not is_subtype_of(bool, str))
|
||||||
static_assert(not is_subtype_of_given(True, bool, str))
|
static_assert(not ConstraintSet.always().implies_subtype_of(bool, str))
|
||||||
```
|
```
|
||||||
|
|
||||||
Moreover, for concrete types, the answer does not depend on which constraint set we are considering.
|
Moreover, for concrete types, the answer does not depend on which constraint set we are considering.
|
||||||
|
|
@ -32,16 +32,16 @@ there isn't a valid specialization for the typevars we are considering.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Never
|
from typing import Never
|
||||||
from ty_extensions import range_constraint
|
from ty_extensions import ConstraintSet
|
||||||
|
|
||||||
def even_given_constraints[T]():
|
def even_given_constraints[T]():
|
||||||
constraints = range_constraint(Never, T, int)
|
constraints = ConstraintSet.range(Never, T, int)
|
||||||
static_assert(is_subtype_of_given(constraints, bool, int))
|
static_assert(constraints.implies_subtype_of(bool, int))
|
||||||
static_assert(not is_subtype_of_given(constraints, bool, str))
|
static_assert(not constraints.implies_subtype_of(bool, str))
|
||||||
|
|
||||||
def even_given_unsatisfiable_constraints():
|
def even_given_unsatisfiable_constraints():
|
||||||
static_assert(is_subtype_of_given(False, bool, int))
|
static_assert(ConstraintSet.never().implies_subtype_of(bool, int))
|
||||||
static_assert(not is_subtype_of_given(False, bool, str))
|
static_assert(not ConstraintSet.never().implies_subtype_of(bool, str))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Type variables
|
## Type variables
|
||||||
|
|
@ -141,37 +141,37 @@ considering.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Never
|
from typing import Never
|
||||||
from ty_extensions import is_subtype_of_given, range_constraint, static_assert
|
from ty_extensions import ConstraintSet, static_assert
|
||||||
|
|
||||||
def given_constraints[T]():
|
def given_constraints[T]():
|
||||||
static_assert(not is_subtype_of_given(True, T, int))
|
static_assert(not ConstraintSet.always().implies_subtype_of(T, int))
|
||||||
static_assert(not is_subtype_of_given(True, T, bool))
|
static_assert(not ConstraintSet.always().implies_subtype_of(T, bool))
|
||||||
static_assert(not is_subtype_of_given(True, T, str))
|
static_assert(not ConstraintSet.always().implies_subtype_of(T, str))
|
||||||
|
|
||||||
# These are vacuously true; false implies anything
|
# These are vacuously true; false implies anything
|
||||||
static_assert(is_subtype_of_given(False, T, int))
|
static_assert(ConstraintSet.never().implies_subtype_of(T, int))
|
||||||
static_assert(is_subtype_of_given(False, T, bool))
|
static_assert(ConstraintSet.never().implies_subtype_of(T, bool))
|
||||||
static_assert(is_subtype_of_given(False, T, str))
|
static_assert(ConstraintSet.never().implies_subtype_of(T, str))
|
||||||
|
|
||||||
given_int = range_constraint(Never, T, int)
|
given_int = ConstraintSet.range(Never, T, int)
|
||||||
static_assert(is_subtype_of_given(given_int, T, int))
|
static_assert(given_int.implies_subtype_of(T, int))
|
||||||
static_assert(not is_subtype_of_given(given_int, T, bool))
|
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||||
static_assert(not is_subtype_of_given(given_int, T, str))
|
static_assert(not given_int.implies_subtype_of(T, str))
|
||||||
|
|
||||||
given_bool = range_constraint(Never, T, bool)
|
given_bool = ConstraintSet.range(Never, T, bool)
|
||||||
static_assert(is_subtype_of_given(given_bool, T, int))
|
static_assert(given_bool.implies_subtype_of(T, int))
|
||||||
static_assert(is_subtype_of_given(given_bool, T, bool))
|
static_assert(given_bool.implies_subtype_of(T, bool))
|
||||||
static_assert(not is_subtype_of_given(given_bool, T, str))
|
static_assert(not given_bool.implies_subtype_of(T, str))
|
||||||
|
|
||||||
given_both = given_bool & given_int
|
given_both = given_bool & given_int
|
||||||
static_assert(is_subtype_of_given(given_both, T, int))
|
static_assert(given_both.implies_subtype_of(T, int))
|
||||||
static_assert(is_subtype_of_given(given_both, T, bool))
|
static_assert(given_both.implies_subtype_of(T, bool))
|
||||||
static_assert(not is_subtype_of_given(given_both, T, str))
|
static_assert(not given_both.implies_subtype_of(T, str))
|
||||||
|
|
||||||
given_str = range_constraint(Never, T, str)
|
given_str = ConstraintSet.range(Never, T, str)
|
||||||
static_assert(not is_subtype_of_given(given_str, T, int))
|
static_assert(not given_str.implies_subtype_of(T, int))
|
||||||
static_assert(not is_subtype_of_given(given_str, T, bool))
|
static_assert(not given_str.implies_subtype_of(T, bool))
|
||||||
static_assert(is_subtype_of_given(given_str, T, str))
|
static_assert(given_str.implies_subtype_of(T, str))
|
||||||
```
|
```
|
||||||
|
|
||||||
This might require propagating constraints from other typevars.
|
This might require propagating constraints from other typevars.
|
||||||
|
|
@ -179,20 +179,20 @@ This might require propagating constraints from other typevars.
|
||||||
```py
|
```py
|
||||||
def mutually_constrained[T, U]():
|
def mutually_constrained[T, U]():
|
||||||
# If [T = U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
# If [T = U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
||||||
given_int = range_constraint(U, T, U) & range_constraint(Never, U, int)
|
given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
# TODO: no static-assert-error
|
# TODO: no static-assert-error
|
||||||
# error: [static-assert-error]
|
# error: [static-assert-error]
|
||||||
static_assert(is_subtype_of_given(given_int, T, int))
|
static_assert(given_int.implies_subtype_of(T, int))
|
||||||
static_assert(not is_subtype_of_given(given_int, T, bool))
|
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||||
static_assert(not is_subtype_of_given(given_int, T, str))
|
static_assert(not given_int.implies_subtype_of(T, str))
|
||||||
|
|
||||||
# If [T ≤ U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
# If [T ≤ U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
||||||
given_int = range_constraint(Never, T, U) & range_constraint(Never, U, int)
|
given_int = ConstraintSet.range(Never, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
# TODO: no static-assert-error
|
# TODO: no static-assert-error
|
||||||
# error: [static-assert-error]
|
# error: [static-assert-error]
|
||||||
static_assert(is_subtype_of_given(given_int, T, int))
|
static_assert(given_int.implies_subtype_of(T, int))
|
||||||
static_assert(not is_subtype_of_given(given_int, T, bool))
|
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||||
static_assert(not is_subtype_of_given(given_int, T, str))
|
static_assert(not given_int.implies_subtype_of(T, str))
|
||||||
```
|
```
|
||||||
|
|
||||||
[subtyping]: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
[subtyping]: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
||||||
|
|
@ -4119,6 +4119,39 @@ impl<'db> Type<'db> {
|
||||||
Place::bound(Type::KnownBoundMethod(KnownBoundMethodType::PathOpen)).into()
|
Place::bound(Type::KnownBoundMethod(KnownBoundMethodType::PathOpen)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type::ClassLiteral(class)
|
||||||
|
if name == "range" && class.is_known(db, KnownClass::ConstraintSet) =>
|
||||||
|
{
|
||||||
|
Place::bound(Type::KnownBoundMethod(
|
||||||
|
KnownBoundMethodType::ConstraintSetRange,
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Type::ClassLiteral(class)
|
||||||
|
if name == "always" && class.is_known(db, KnownClass::ConstraintSet) =>
|
||||||
|
{
|
||||||
|
Place::bound(Type::KnownBoundMethod(
|
||||||
|
KnownBoundMethodType::ConstraintSetAlways,
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Type::ClassLiteral(class)
|
||||||
|
if name == "never" && class.is_known(db, KnownClass::ConstraintSet) =>
|
||||||
|
{
|
||||||
|
Place::bound(Type::KnownBoundMethod(
|
||||||
|
KnownBoundMethodType::ConstraintSetNever,
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked))
|
||||||
|
if name == "implies_subtype_of" =>
|
||||||
|
{
|
||||||
|
Place::bound(Type::KnownBoundMethod(
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked),
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
Type::ClassLiteral(class)
|
Type::ClassLiteral(class)
|
||||||
if name == "__get__" && class.is_known(db, KnownClass::FunctionType) =>
|
if name == "__get__" && class.is_known(db, KnownClass::FunctionType) =>
|
||||||
{
|
{
|
||||||
|
|
@ -4833,51 +4866,6 @@ impl<'db> Type<'db> {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
||||||
Some(KnownFunction::IsSubtypeOfGiven) => Binding::single(
|
|
||||||
self,
|
|
||||||
Signature::new(
|
|
||||||
Parameters::new([
|
|
||||||
Parameter::positional_only(Some(Name::new_static("constraints")))
|
|
||||||
.with_annotated_type(UnionType::from_elements(
|
|
||||||
db,
|
|
||||||
[
|
|
||||||
KnownClass::Bool.to_instance(db),
|
|
||||||
KnownClass::ConstraintSet.to_instance(db),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("ty")))
|
|
||||||
.type_form()
|
|
||||||
.with_annotated_type(Type::any()),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("of")))
|
|
||||||
.type_form()
|
|
||||||
.with_annotated_type(Type::any()),
|
|
||||||
]),
|
|
||||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
|
|
||||||
Some(KnownFunction::RangeConstraint | KnownFunction::NegatedRangeConstraint) => {
|
|
||||||
Binding::single(
|
|
||||||
self,
|
|
||||||
Signature::new(
|
|
||||||
Parameters::new([
|
|
||||||
Parameter::positional_only(Some(Name::new_static("lower_bound")))
|
|
||||||
.type_form()
|
|
||||||
.with_annotated_type(Type::any()),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("typevar")))
|
|
||||||
.type_form()
|
|
||||||
.with_annotated_type(Type::any()),
|
|
||||||
Parameter::positional_only(Some(Name::new_static("upper_bound")))
|
|
||||||
.type_form()
|
|
||||||
.with_annotated_type(Type::any()),
|
|
||||||
]),
|
|
||||||
Some(KnownClass::ConstraintSet.to_instance(db)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(KnownFunction::IsSingleton | KnownFunction::IsSingleValued) => {
|
Some(KnownFunction::IsSingleton | KnownFunction::IsSingleValued) => {
|
||||||
Binding::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
|
|
@ -6918,7 +6906,14 @@ impl<'db> Type<'db> {
|
||||||
| Type::AlwaysTruthy
|
| Type::AlwaysTruthy
|
||||||
| Type::AlwaysFalsy
|
| Type::AlwaysFalsy
|
||||||
| Type::WrapperDescriptor(_)
|
| Type::WrapperDescriptor(_)
|
||||||
| Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen)
|
| Type::KnownBoundMethod(
|
||||||
|
KnownBoundMethodType::StrStartswith(_)
|
||||||
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
|
||||||
|
)
|
||||||
| Type::DataclassDecorator(_)
|
| Type::DataclassDecorator(_)
|
||||||
| Type::DataclassTransformer(_)
|
| Type::DataclassTransformer(_)
|
||||||
// A non-generic class never needs to be specialized. A generic class is specialized
|
// A non-generic class never needs to be specialized. A generic class is specialized
|
||||||
|
|
@ -7064,7 +7059,12 @@ impl<'db> Type<'db> {
|
||||||
| Type::AlwaysFalsy
|
| Type::AlwaysFalsy
|
||||||
| Type::WrapperDescriptor(_)
|
| Type::WrapperDescriptor(_)
|
||||||
| Type::KnownBoundMethod(
|
| Type::KnownBoundMethod(
|
||||||
KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen,
|
KnownBoundMethodType::StrStartswith(_)
|
||||||
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
)
|
)
|
||||||
| Type::DataclassDecorator(_)
|
| Type::DataclassDecorator(_)
|
||||||
| Type::DataclassTransformer(_)
|
| Type::DataclassTransformer(_)
|
||||||
|
|
@ -10318,6 +10318,12 @@ pub enum KnownBoundMethodType<'db> {
|
||||||
StrStartswith(StringLiteralType<'db>),
|
StrStartswith(StringLiteralType<'db>),
|
||||||
/// Method wrapper for `Path.open`,
|
/// Method wrapper for `Path.open`,
|
||||||
PathOpen,
|
PathOpen,
|
||||||
|
|
||||||
|
// ConstraintSet methods
|
||||||
|
ConstraintSetRange,
|
||||||
|
ConstraintSetAlways,
|
||||||
|
ConstraintSetNever,
|
||||||
|
ConstraintSetImpliesSubtypeOf(TrackedConstraintSet<'db>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
|
|
@ -10341,7 +10347,11 @@ pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Size
|
||||||
KnownBoundMethodType::StrStartswith(string_literal) => {
|
KnownBoundMethodType::StrStartswith(string_literal) => {
|
||||||
visitor.visit_type(db, Type::StringLiteral(string_literal));
|
visitor.visit_type(db, Type::StringLiteral(string_literal));
|
||||||
}
|
}
|
||||||
KnownBoundMethodType::PathOpen => {}
|
KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -10393,9 +10403,23 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
ConstraintSet::from(self == other)
|
ConstraintSet::from(self == other)
|
||||||
}
|
}
|
||||||
|
|
||||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen) => {
|
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen)
|
||||||
ConstraintSet::from(true)
|
| (
|
||||||
}
|
KnownBoundMethodType::ConstraintSetRange,
|
||||||
|
KnownBoundMethodType::ConstraintSetRange,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
KnownBoundMethodType::ConstraintSetAlways,
|
||||||
|
KnownBoundMethodType::ConstraintSetAlways,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
KnownBoundMethodType::ConstraintSetNever,
|
||||||
|
KnownBoundMethodType::ConstraintSetNever,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
|
) => ConstraintSet::from(true),
|
||||||
|
|
||||||
(
|
(
|
||||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||||
|
|
@ -10403,13 +10427,21 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
| KnownBoundMethodType::PropertyDunderGet(_)
|
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||||
| KnownBoundMethodType::StrStartswith(_)
|
| KnownBoundMethodType::StrStartswith(_)
|
||||||
| KnownBoundMethodType::PathOpen,
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||||
| KnownBoundMethodType::FunctionTypeDunderCall(_)
|
| KnownBoundMethodType::FunctionTypeDunderCall(_)
|
||||||
| KnownBoundMethodType::PropertyDunderGet(_)
|
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||||
| KnownBoundMethodType::StrStartswith(_)
|
| KnownBoundMethodType::StrStartswith(_)
|
||||||
| KnownBoundMethodType::PathOpen,
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
) => ConstraintSet::from(false),
|
) => ConstraintSet::from(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10445,9 +10477,26 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
ConstraintSet::from(self == other)
|
ConstraintSet::from(self == other)
|
||||||
}
|
}
|
||||||
|
|
||||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen) => {
|
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen)
|
||||||
ConstraintSet::from(true)
|
| (
|
||||||
}
|
KnownBoundMethodType::ConstraintSetRange,
|
||||||
|
KnownBoundMethodType::ConstraintSetRange,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
KnownBoundMethodType::ConstraintSetAlways,
|
||||||
|
KnownBoundMethodType::ConstraintSetAlways,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
KnownBoundMethodType::ConstraintSetNever,
|
||||||
|
KnownBoundMethodType::ConstraintSetNever,
|
||||||
|
) => ConstraintSet::from(true),
|
||||||
|
|
||||||
|
(
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(left_constraints),
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(right_constraints),
|
||||||
|
) => left_constraints
|
||||||
|
.constraints(db)
|
||||||
|
.iff(db, right_constraints.constraints(db)),
|
||||||
|
|
||||||
(
|
(
|
||||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||||
|
|
@ -10455,13 +10504,21 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
| KnownBoundMethodType::PropertyDunderGet(_)
|
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||||
| KnownBoundMethodType::StrStartswith(_)
|
| KnownBoundMethodType::StrStartswith(_)
|
||||||
| KnownBoundMethodType::PathOpen,
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||||
| KnownBoundMethodType::FunctionTypeDunderCall(_)
|
| KnownBoundMethodType::FunctionTypeDunderCall(_)
|
||||||
| KnownBoundMethodType::PropertyDunderGet(_)
|
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||||
| KnownBoundMethodType::StrStartswith(_)
|
| KnownBoundMethodType::StrStartswith(_)
|
||||||
| KnownBoundMethodType::PathOpen,
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||||
) => ConstraintSet::from(false),
|
) => ConstraintSet::from(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10480,7 +10537,12 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
KnownBoundMethodType::PropertyDunderSet(property) => {
|
KnownBoundMethodType::PropertyDunderSet(property) => {
|
||||||
KnownBoundMethodType::PropertyDunderSet(property.normalized_impl(db, visitor))
|
KnownBoundMethodType::PropertyDunderSet(property.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen => self,
|
KnownBoundMethodType::StrStartswith(_)
|
||||||
|
| KnownBoundMethodType::PathOpen
|
||||||
|
| KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -10493,6 +10555,10 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
| KnownBoundMethodType::PropertyDunderSet(_) => KnownClass::MethodWrapperType,
|
| KnownBoundMethodType::PropertyDunderSet(_) => KnownClass::MethodWrapperType,
|
||||||
KnownBoundMethodType::StrStartswith(_) => KnownClass::BuiltinFunctionType,
|
KnownBoundMethodType::StrStartswith(_) => KnownClass::BuiltinFunctionType,
|
||||||
KnownBoundMethodType::PathOpen => KnownClass::MethodType,
|
KnownBoundMethodType::PathOpen => KnownClass::MethodType,
|
||||||
|
KnownBoundMethodType::ConstraintSetRange
|
||||||
|
| KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever
|
||||||
|
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => KnownClass::ConstraintSet,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -10592,6 +10658,45 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
KnownBoundMethodType::PathOpen => {
|
KnownBoundMethodType::PathOpen => {
|
||||||
Either::Right(std::iter::once(Signature::todo("`Path.open` return type")))
|
Either::Right(std::iter::once(Signature::todo("`Path.open` return type")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KnownBoundMethodType::ConstraintSetRange => {
|
||||||
|
Either::Right(std::iter::once(Signature::new(
|
||||||
|
Parameters::new([
|
||||||
|
Parameter::positional_only(Some(Name::new_static("lower_bound")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("typevar")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("upper_bound")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
]),
|
||||||
|
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
KnownBoundMethodType::ConstraintSetAlways
|
||||||
|
| KnownBoundMethodType::ConstraintSetNever => {
|
||||||
|
Either::Right(std::iter::once(Signature::new(
|
||||||
|
Parameters::empty(),
|
||||||
|
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => {
|
||||||
|
Either::Right(std::iter::once(Signature::new(
|
||||||
|
Parameters::new([
|
||||||
|
Parameter::positional_only(Some(Name::new_static("ty")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("of")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
]),
|
||||||
|
Some(KnownClass::ConstraintSet.to_instance(db)),
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -705,33 +705,6 @@ impl<'db> Bindings<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownFunction::IsSubtypeOfGiven) => {
|
|
||||||
let [Some(constraints), Some(ty_a), Some(ty_b)] =
|
|
||||||
overload.parameter_types()
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let constraints = match constraints {
|
|
||||||
Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked)) => {
|
|
||||||
tracked.constraints(db)
|
|
||||||
}
|
|
||||||
Type::BooleanLiteral(b) => ConstraintSet::from(*b),
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = constraints.when_subtype_of_given(
|
|
||||||
db,
|
|
||||||
*ty_a,
|
|
||||||
*ty_b,
|
|
||||||
InferableTypeVars::None,
|
|
||||||
);
|
|
||||||
let tracked = TrackedConstraintSet::new(db, result);
|
|
||||||
overload.set_return_type(Type::KnownInstance(
|
|
||||||
KnownInstanceType::ConstraintSet(tracked),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(KnownFunction::IsAssignableTo) => {
|
Some(KnownFunction::IsAssignableTo) => {
|
||||||
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
|
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
|
||||||
let constraints =
|
let constraints =
|
||||||
|
|
@ -1149,6 +1122,60 @@ impl<'db> Bindings<'db> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetRange) => {
|
||||||
|
let [Some(lower), Some(Type::TypeVar(typevar)), Some(upper)] =
|
||||||
|
overload.parameter_types()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let constraints = ConstraintSet::range(db, *lower, *typevar, *upper);
|
||||||
|
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||||
|
overload.set_return_type(Type::KnownInstance(
|
||||||
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetAlways) => {
|
||||||
|
if !overload.parameter_types().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let constraints = ConstraintSet::from(true);
|
||||||
|
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||||
|
overload.set_return_type(Type::KnownInstance(
|
||||||
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => {
|
||||||
|
if !overload.parameter_types().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let constraints = ConstraintSet::from(false);
|
||||||
|
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||||
|
overload.set_return_type(Type::KnownInstance(
|
||||||
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::KnownBoundMethod(
|
||||||
|
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked),
|
||||||
|
) => {
|
||||||
|
let [Some(ty_a), Some(ty_b)] = overload.parameter_types() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = tracked.constraints(db).when_subtype_of_given(
|
||||||
|
db,
|
||||||
|
*ty_a,
|
||||||
|
*ty_b,
|
||||||
|
InferableTypeVars::None,
|
||||||
|
);
|
||||||
|
let tracked = TrackedConstraintSet::new(db, result);
|
||||||
|
overload.set_return_type(Type::KnownInstance(
|
||||||
|
KnownInstanceType::ConstraintSet(tracked),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Type::ClassLiteral(class) => match class.known(db) {
|
Type::ClassLiteral(class) => match class.known(db) {
|
||||||
Some(KnownClass::Bool) => match overload.parameter_types() {
|
Some(KnownClass::Bool) => match overload.parameter_types() {
|
||||||
[Some(arg)] => overload.set_return_type(arg.bool(db).into_type(db)),
|
[Some(arg)] => overload.set_return_type(arg.bool(db).into_type(db)),
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,12 @@ impl<'db> ConstraintSet<'db> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iff(self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
|
ConstraintSet {
|
||||||
|
node: self.node.iff(db, other.node),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn range(
|
pub(crate) fn range(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
lower: Type<'db>,
|
lower: Type<'db>,
|
||||||
|
|
@ -304,15 +310,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
Self::constrain_typevar(db, typevar, lower, upper, TypeRelation::Assignability)
|
Self::constrain_typevar(db, typevar, lower, upper, TypeRelation::Assignability)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn negated_range(
|
|
||||||
db: &'db dyn Db,
|
|
||||||
lower: Type<'db>,
|
|
||||||
typevar: BoundTypeVarInstance<'db>,
|
|
||||||
upper: Type<'db>,
|
|
||||||
) -> Self {
|
|
||||||
Self::range(db, lower, typevar, upper).negate(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
|
pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
|
||||||
self.node.simplify(db).display(db)
|
self.node.simplify(db).display(db)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -523,6 +523,18 @@ impl Display for DisplayRepresentation<'_> {
|
||||||
Type::KnownBoundMethod(KnownBoundMethodType::PathOpen) => {
|
Type::KnownBoundMethod(KnownBoundMethodType::PathOpen) => {
|
||||||
f.write_str("bound method `Path.open`")
|
f.write_str("bound method `Path.open`")
|
||||||
}
|
}
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetRange) => {
|
||||||
|
f.write_str("bound method `ConstraintSet.range`")
|
||||||
|
}
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetAlways) => {
|
||||||
|
f.write_str("bound method `ConstraintSet.always`")
|
||||||
|
}
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => {
|
||||||
|
f.write_str("bound method `ConstraintSet.never`")
|
||||||
|
}
|
||||||
|
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)) => {
|
||||||
|
f.write_str("bound method `ConstraintSet.implies_subtype_of`")
|
||||||
|
}
|
||||||
Type::WrapperDescriptor(kind) => {
|
Type::WrapperDescriptor(kind) => {
|
||||||
let (method, object) = match kind {
|
let (method, object) = match kind {
|
||||||
WrapperDescriptorKind::FunctionTypeDunderGet => ("__get__", "function"),
|
WrapperDescriptorKind::FunctionTypeDunderGet => ("__get__", "function"),
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,9 @@ use crate::types::visitor::any_over_type;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase,
|
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase,
|
||||||
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
|
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
|
||||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
|
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, NormalizedVisitor,
|
||||||
NormalizedVisitor, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext,
|
SpecialFormType, Truthiness, Type, TypeContext, TypeMapping, TypeRelation, UnionBuilder,
|
||||||
TypeMapping, TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature,
|
binding_type, todo_type, walk_signature,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||||
|
|
||||||
|
|
@ -1299,8 +1299,6 @@ pub enum KnownFunction {
|
||||||
IsEquivalentTo,
|
IsEquivalentTo,
|
||||||
/// `ty_extensions.is_subtype_of`
|
/// `ty_extensions.is_subtype_of`
|
||||||
IsSubtypeOf,
|
IsSubtypeOf,
|
||||||
/// `ty_extensions.is_subtype_of_given`
|
|
||||||
IsSubtypeOfGiven,
|
|
||||||
/// `ty_extensions.is_assignable_to`
|
/// `ty_extensions.is_assignable_to`
|
||||||
IsAssignableTo,
|
IsAssignableTo,
|
||||||
/// `ty_extensions.is_disjoint_from`
|
/// `ty_extensions.is_disjoint_from`
|
||||||
|
|
@ -1323,10 +1321,6 @@ pub enum KnownFunction {
|
||||||
RevealProtocolInterface,
|
RevealProtocolInterface,
|
||||||
/// `ty_extensions.reveal_mro`
|
/// `ty_extensions.reveal_mro`
|
||||||
RevealMro,
|
RevealMro,
|
||||||
/// `ty_extensions.range_constraint`
|
|
||||||
RangeConstraint,
|
|
||||||
/// `ty_extensions.negated_range_constraint`
|
|
||||||
NegatedRangeConstraint,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KnownFunction {
|
impl KnownFunction {
|
||||||
|
|
@ -1393,15 +1387,12 @@ impl KnownFunction {
|
||||||
| Self::IsSingleValued
|
| Self::IsSingleValued
|
||||||
| Self::IsSingleton
|
| Self::IsSingleton
|
||||||
| Self::IsSubtypeOf
|
| Self::IsSubtypeOf
|
||||||
| Self::IsSubtypeOfGiven
|
|
||||||
| Self::GenericContext
|
| Self::GenericContext
|
||||||
| Self::DunderAllNames
|
| Self::DunderAllNames
|
||||||
| Self::EnumMembers
|
| Self::EnumMembers
|
||||||
| Self::StaticAssert
|
| Self::StaticAssert
|
||||||
| Self::HasMember
|
| Self::HasMember
|
||||||
| Self::RevealProtocolInterface
|
| Self::RevealProtocolInterface
|
||||||
| Self::RangeConstraint
|
|
||||||
| Self::NegatedRangeConstraint
|
|
||||||
| Self::RevealMro
|
| Self::RevealMro
|
||||||
| Self::AllMembers => module.is_ty_extensions(),
|
| Self::AllMembers => module.is_ty_extensions(),
|
||||||
Self::ImportModule => module.is_importlib(),
|
Self::ImportModule => module.is_importlib(),
|
||||||
|
|
@ -1780,32 +1771,6 @@ impl KnownFunction {
|
||||||
overload.set_return_type(Type::module_literal(db, file, module));
|
overload.set_return_type(Type::module_literal(db, file, module));
|
||||||
}
|
}
|
||||||
|
|
||||||
KnownFunction::RangeConstraint => {
|
|
||||||
let [Some(lower), Some(Type::TypeVar(typevar)), Some(upper)] = parameter_types
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let constraints = ConstraintSet::range(db, *lower, *typevar, *upper);
|
|
||||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
|
||||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
|
|
||||||
tracked,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
KnownFunction::NegatedRangeConstraint => {
|
|
||||||
let [Some(lower), Some(Type::TypeVar(typevar)), Some(upper)] = parameter_types
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let constraints = ConstraintSet::negated_range(db, *lower, *typevar, *upper);
|
|
||||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
|
||||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
|
|
||||||
tracked,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
KnownFunction::Open => {
|
KnownFunction::Open => {
|
||||||
// TODO: Temporary special-casing for `builtins.open` to avoid an excessive number of
|
// TODO: Temporary special-casing for `builtins.open` to avoid an excessive number of
|
||||||
// false positives in lieu of proper support for PEP-613 type aliases.
|
// false positives in lieu of proper support for PEP-613 type aliases.
|
||||||
|
|
@ -1894,7 +1859,6 @@ pub(crate) mod tests {
|
||||||
|
|
||||||
KnownFunction::IsSingleton
|
KnownFunction::IsSingleton
|
||||||
| KnownFunction::IsSubtypeOf
|
| KnownFunction::IsSubtypeOf
|
||||||
| KnownFunction::IsSubtypeOfGiven
|
|
||||||
| KnownFunction::GenericContext
|
| KnownFunction::GenericContext
|
||||||
| KnownFunction::DunderAllNames
|
| KnownFunction::DunderAllNames
|
||||||
| KnownFunction::EnumMembers
|
| KnownFunction::EnumMembers
|
||||||
|
|
@ -1905,8 +1869,6 @@ pub(crate) mod tests {
|
||||||
| KnownFunction::IsEquivalentTo
|
| KnownFunction::IsEquivalentTo
|
||||||
| KnownFunction::HasMember
|
| KnownFunction::HasMember
|
||||||
| KnownFunction::RevealProtocolInterface
|
| KnownFunction::RevealProtocolInterface
|
||||||
| KnownFunction::RangeConstraint
|
|
||||||
| KnownFunction::NegatedRangeConstraint
|
|
||||||
| KnownFunction::RevealMro
|
| KnownFunction::RevealMro
|
||||||
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,29 @@ type JustComplex = TypeOf[1.0j]
|
||||||
|
|
||||||
# Constraints
|
# Constraints
|
||||||
class ConstraintSet:
|
class ConstraintSet:
|
||||||
|
@staticmethod
|
||||||
|
def range(lower_bound: Any, typevar: Any, upper_bound: Any) -> Self:
|
||||||
|
"""
|
||||||
|
Returns a constraint set that requires `typevar` to specialize to a type
|
||||||
|
that is a supertype of `lower_bound` and a subtype of `upper_bound`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def always() -> Self:
|
||||||
|
"""Returns a constraint set that is always satisfied"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def never() -> Self:
|
||||||
|
"""Returns a constraint set that is never satisfied"""
|
||||||
|
|
||||||
|
def implies_subtype_of(self, ty: Any, of: Any) -> Self:
|
||||||
|
"""
|
||||||
|
Returns a constraint set that is satisfied when `ty` is a `subtype`_ of
|
||||||
|
`of`, assuming that all of the constraints in `self` hold.
|
||||||
|
|
||||||
|
.. _subtype: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
||||||
|
"""
|
||||||
|
|
||||||
def __bool__(self) -> bool: ...
|
def __bool__(self) -> bool: ...
|
||||||
def __eq__(self, other: ConstraintSet) -> bool: ...
|
def __eq__(self, other: ConstraintSet) -> bool: ...
|
||||||
def __ne__(self, other: ConstraintSet) -> bool: ...
|
def __ne__(self, other: ConstraintSet) -> bool: ...
|
||||||
|
|
@ -51,13 +74,6 @@ class ConstraintSet:
|
||||||
def __or__(self, other: ConstraintSet) -> ConstraintSet: ...
|
def __or__(self, other: ConstraintSet) -> ConstraintSet: ...
|
||||||
def __invert__(self) -> ConstraintSet: ...
|
def __invert__(self) -> ConstraintSet: ...
|
||||||
|
|
||||||
def range_constraint(
|
|
||||||
lower_bound: Any, typevar: Any, upper_bound: Any
|
|
||||||
) -> ConstraintSet: ...
|
|
||||||
def negated_range_constraint(
|
|
||||||
lower_bound: Any, typevar: Any, upper_bound: Any
|
|
||||||
) -> ConstraintSet: ...
|
|
||||||
|
|
||||||
# Predicates on types
|
# Predicates on types
|
||||||
#
|
#
|
||||||
# Ideally, these would be annotated using `TypeForm`, but that has not been
|
# Ideally, these would be annotated using `TypeForm`, but that has not been
|
||||||
|
|
@ -75,15 +91,6 @@ def is_subtype_of(ty: Any, of: Any) -> ConstraintSet:
|
||||||
.. _subtype: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
.. _subtype: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def is_subtype_of_given(
|
|
||||||
constraints: bool | ConstraintSet, ty: Any, of: Any
|
|
||||||
) -> ConstraintSet:
|
|
||||||
"""Returns a constraint set that is satisfied when `ty` is a `subtype`_ of `of`,
|
|
||||||
assuming that all of the constraints in `constraints` hold.
|
|
||||||
|
|
||||||
.. _subtype: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
|
||||||
"""
|
|
||||||
|
|
||||||
def is_assignable_to(ty: Any, to: Any) -> ConstraintSet:
|
def is_assignable_to(ty: Any, to: Any) -> ConstraintSet:
|
||||||
"""Returns a constraint set that is satisfied when `ty` is `assignable`_ to `to`.
|
"""Returns a constraint set that is satisfied when `ty` is `assignable`_ to `to`.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue