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
|
||||
from typing import Any, final, Never, Sequence
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -45,7 +45,7 @@ class Unrelated: ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -54,7 +54,7 @@ bound.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -63,7 +63,7 @@ no upper bound.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -72,7 +72,7 @@ constrain the typevar at all.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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)
|
||||
|
|
@ -81,9 +81,9 @@ or incomparable, then there is no type that can satisfy the constraint.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
reveal_type(range_constraint(Super, T, Sub))
|
||||
reveal_type(ConstraintSet.range(Super, T, Sub))
|
||||
# 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
|
||||
|
|
@ -92,7 +92,7 @@ that specific type.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -101,14 +101,14 @@ their bottom and top materializations, respectively.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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])]
|
||||
reveal_type(range_constraint(Sequence[Base], T, Sequence[Any]))
|
||||
reveal_type(ConstraintSet.range(Sequence[Base], T, Sequence[Any]))
|
||||
|
||||
# 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])]
|
||||
reveal_type(range_constraint(Sequence[Any], T, Sequence[Base]))
|
||||
reveal_type(ConstraintSet.range(Sequence[Any], T, Sequence[Base]))
|
||||
```
|
||||
|
||||
### Negated range
|
||||
|
|
@ -119,7 +119,7 @@ strict subtype of the lower bound, a strict supertype of the upper bound, or inc
|
|||
|
||||
```py
|
||||
from typing import Any, final, Never, Sequence
|
||||
from ty_extensions import negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -130,7 +130,7 @@ class Unrelated: ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -139,7 +139,7 @@ bound.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -148,7 +148,7 @@ no upper bound.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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`
|
||||
|
|
@ -157,7 +157,7 @@ cannot be satisfied at all.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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)
|
||||
|
|
@ -166,9 +166,9 @@ or incomparable, then the negated range constraint can always be satisfied.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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]
|
||||
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
|
||||
|
|
@ -177,7 +177,7 @@ type other than that specific type.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -186,14 +186,14 @@ their bottom and top materializations, respectively.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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])]
|
||||
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)]
|
||||
reveal_type(negated_range_constraint(Any, T, Base))
|
||||
reveal_type(~ConstraintSet.range(Any, T, 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
|
||||
|
|
@ -204,7 +204,7 @@ cases, we can simplify the result of an intersection.
|
|||
### Different typevars
|
||||
|
||||
```py
|
||||
from ty_extensions import range_constraint, negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -216,9 +216,9 @@ We cannot simplify the intersection of constraints that refer to different typev
|
|||
```py
|
||||
def _[T, U]() -> None:
|
||||
# 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))]
|
||||
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
|
||||
|
|
@ -227,7 +227,7 @@ The intersection of two ranges is where the ranges "overlap".
|
|||
|
||||
```py
|
||||
from typing import final
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -239,13 +239,13 @@ class Unrelated: ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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)]
|
||||
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)]
|
||||
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.
|
||||
|
|
@ -253,9 +253,9 @@ If they don't overlap, the intersection is empty.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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]
|
||||
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
|
||||
|
|
@ -266,7 +266,7 @@ the intersection as removing the hole from the range constraint.
|
|||
|
||||
```py
|
||||
from typing import final, Never
|
||||
from ty_extensions import range_constraint, negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -282,9 +282,9 @@ If the negative range completely contains the positive range, then the intersect
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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]
|
||||
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
|
||||
|
|
@ -293,11 +293,11 @@ anything; the intersection is the positive range.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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)]
|
||||
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
|
||||
|
|
@ -306,9 +306,9 @@ range.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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))]
|
||||
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
|
||||
|
|
@ -318,7 +318,7 @@ smaller constraint. For negated ranges, the smaller constraint is the one with t
|
|||
|
||||
```py
|
||||
from typing import final
|
||||
from ty_extensions import negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -330,9 +330,9 @@ class Unrelated: ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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.
|
||||
|
|
@ -340,11 +340,11 @@ Otherwise, the union cannot be simplified.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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))]
|
||||
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@_))]
|
||||
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
|
||||
|
|
@ -361,7 +361,7 @@ that type _is_ in `SubSub ≤ T ≤ Super`, it is not correct to simplify the un
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -372,7 +372,7 @@ can simplify the result of an union.
|
|||
### Different typevars
|
||||
|
||||
```py
|
||||
from ty_extensions import range_constraint, negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -384,9 +384,9 @@ We cannot simplify the union of constraints that refer to different typevars.
|
|||
```py
|
||||
def _[T, U]() -> None:
|
||||
# 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)]
|
||||
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
|
||||
|
|
@ -396,7 +396,7 @@ bounds.
|
|||
|
||||
```py
|
||||
from typing import final
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -408,9 +408,9 @@ class Unrelated: ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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.
|
||||
|
|
@ -418,11 +418,11 @@ Otherwise, the union cannot be simplified.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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@_)]
|
||||
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
|
||||
|
|
@ -438,7 +438,7 @@ not include `Sub`. That means it should not be in the union. Since that type _is
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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
|
||||
|
|
@ -449,7 +449,7 @@ the union as filling part of the hole with the types from the range constraint.
|
|||
|
||||
```py
|
||||
from typing import final, Never
|
||||
from ty_extensions import range_constraint, negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -465,9 +465,9 @@ If the positive range completely contains the negative range, then the union is
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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]
|
||||
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;
|
||||
|
|
@ -476,11 +476,11 @@ the union is the negative range.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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)]
|
||||
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
|
||||
|
|
@ -489,9 +489,9 @@ range.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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
|
||||
|
|
@ -500,7 +500,7 @@ The union of two negated ranges has a hole where the ranges "overlap".
|
|||
|
||||
```py
|
||||
from typing import final
|
||||
from ty_extensions import negated_range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -512,13 +512,13 @@ class Unrelated: ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
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)]
|
||||
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)]
|
||||
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.
|
||||
|
|
@ -526,9 +526,9 @@ If the holes don't overlap, the union is always satisfied.
|
|||
```py
|
||||
def _[T]() -> None:
|
||||
# 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]
|
||||
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
|
||||
|
|
@ -537,7 +537,7 @@ def _[T]() -> None:
|
|||
|
||||
```py
|
||||
from typing import Never
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Super: ...
|
||||
class Base(Super): ...
|
||||
|
|
@ -545,20 +545,20 @@ class Sub(Base): ...
|
|||
|
||||
def _[T]() -> None:
|
||||
# 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)]
|
||||
reveal_type(~range_constraint(Never, T, Base))
|
||||
reveal_type(~ConstraintSet.range(Never, T, Base))
|
||||
# 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]
|
||||
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.
|
||||
|
||||
```py
|
||||
def _[T]() -> None:
|
||||
constraint = range_constraint(Sub, T, Base)
|
||||
constraint = ConstraintSet.range(Sub, T, Base)
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
reveal_type(constraint | ~constraint)
|
||||
```
|
||||
|
|
@ -567,7 +567,7 @@ def _[T]() -> None:
|
|||
|
||||
```py
|
||||
from typing import final, Never
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
class Base: ...
|
||||
|
||||
|
|
@ -576,20 +576,20 @@ class Unrelated: ...
|
|||
|
||||
def _[T, U]() -> None:
|
||||
# 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.
|
||||
|
||||
```py
|
||||
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]
|
||||
reveal_type(c1 | ~c1)
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
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]
|
||||
reveal_type(c2 | ~c2)
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
|
|
@ -614,19 +614,19 @@ since we always hide `Never` lower bounds and `object` upper bounds.
|
|||
|
||||
```py
|
||||
from typing import Never
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
def f[S, T]():
|
||||
# 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)]
|
||||
reveal_type(range_constraint(S, T, object))
|
||||
reveal_type(ConstraintSet.range(S, T, object))
|
||||
|
||||
def f[T, S]():
|
||||
# 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)]
|
||||
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
|
||||
|
|
@ -635,15 +635,15 @@ the constraint, and the other the bound. But we display the result the same way
|
|||
```py
|
||||
def f[S, T]():
|
||||
# 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)]
|
||||
reveal_type(range_constraint(S, T, S))
|
||||
reveal_type(ConstraintSet.range(S, T, S))
|
||||
|
||||
def f[T, S]():
|
||||
# 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)]
|
||||
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
|
||||
|
|
@ -654,7 +654,7 @@ def f[S, T, U]():
|
|||
# Could be either of:
|
||||
# ty_extensions.ConstraintSet[(S@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.
|
||||
|
||||
```py
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
def f[T, U]():
|
||||
t1 = range_constraint(str, T, str)
|
||||
t2 = range_constraint(bool, T, bool)
|
||||
u1 = range_constraint(str, U, str)
|
||||
u2 = range_constraint(bool, U, bool)
|
||||
t1 = ConstraintSet.range(str, T, str)
|
||||
t2 = ConstraintSet.range(bool, T, bool)
|
||||
u1 = ConstraintSet.range(str, U, str)
|
||||
u2 = ConstraintSet.range(bool, U, bool)
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[(T@f = bool) ∨ (T@f = str)]
|
||||
reveal_type(t1 | t2)
|
||||
|
|
@ -692,8 +692,8 @@ from typing import Never
|
|||
from ty_extensions import static_assert
|
||||
|
||||
def f[T]():
|
||||
t_int = range_constraint(Never, T, int)
|
||||
t_bool = range_constraint(Never, T, bool)
|
||||
t_int = ConstraintSet.range(Never, T, int)
|
||||
t_bool = ConstraintSet.range(Never, T, bool)
|
||||
|
||||
# `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`:
|
||||
|
|
@ -707,7 +707,7 @@ def f[T]():
|
|||
# "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
|
||||
# domains!
|
||||
always = range_constraint(Never, T, object)
|
||||
always = ConstraintSet.range(Never, T, object)
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
reveal_type(always)
|
||||
static_assert(always)
|
||||
|
|
@ -721,11 +721,11 @@ intersections whose elements appear in different orders.
|
|||
|
||||
```py
|
||||
from typing import Never
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
def f[T]():
|
||||
# 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)]
|
||||
reveal_type(range_constraint(Never, T, int | str))
|
||||
reveal_type(ConstraintSet.range(Never, T, int | str))
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
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
|
||||
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.)
|
||||
|
||||
```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]():
|
||||
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_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.
|
||||
|
|
@ -32,16 +32,16 @@ there isn't a valid specialization for the typevars we are considering.
|
|||
|
||||
```py
|
||||
from typing import Never
|
||||
from ty_extensions import range_constraint
|
||||
from ty_extensions import ConstraintSet
|
||||
|
||||
def even_given_constraints[T]():
|
||||
constraints = range_constraint(Never, T, int)
|
||||
static_assert(is_subtype_of_given(constraints, bool, int))
|
||||
static_assert(not is_subtype_of_given(constraints, bool, str))
|
||||
constraints = ConstraintSet.range(Never, T, int)
|
||||
static_assert(constraints.implies_subtype_of(bool, int))
|
||||
static_assert(not constraints.implies_subtype_of(bool, str))
|
||||
|
||||
def even_given_unsatisfiable_constraints():
|
||||
static_assert(is_subtype_of_given(False, bool, int))
|
||||
static_assert(not is_subtype_of_given(False, bool, str))
|
||||
static_assert(ConstraintSet.never().implies_subtype_of(bool, int))
|
||||
static_assert(not ConstraintSet.never().implies_subtype_of(bool, str))
|
||||
```
|
||||
|
||||
## Type variables
|
||||
|
|
@ -141,37 +141,37 @@ considering.
|
|||
|
||||
```py
|
||||
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]():
|
||||
static_assert(not is_subtype_of_given(True, T, int))
|
||||
static_assert(not is_subtype_of_given(True, T, bool))
|
||||
static_assert(not is_subtype_of_given(True, T, str))
|
||||
static_assert(not ConstraintSet.always().implies_subtype_of(T, int))
|
||||
static_assert(not ConstraintSet.always().implies_subtype_of(T, bool))
|
||||
static_assert(not ConstraintSet.always().implies_subtype_of(T, str))
|
||||
|
||||
# These are vacuously true; false implies anything
|
||||
static_assert(is_subtype_of_given(False, T, int))
|
||||
static_assert(is_subtype_of_given(False, T, bool))
|
||||
static_assert(is_subtype_of_given(False, T, str))
|
||||
static_assert(ConstraintSet.never().implies_subtype_of(T, int))
|
||||
static_assert(ConstraintSet.never().implies_subtype_of(T, bool))
|
||||
static_assert(ConstraintSet.never().implies_subtype_of(T, str))
|
||||
|
||||
given_int = range_constraint(Never, T, int)
|
||||
static_assert(is_subtype_of_given(given_int, T, int))
|
||||
static_assert(not is_subtype_of_given(given_int, T, bool))
|
||||
static_assert(not is_subtype_of_given(given_int, T, str))
|
||||
given_int = ConstraintSet.range(Never, T, int)
|
||||
static_assert(given_int.implies_subtype_of(T, int))
|
||||
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||
static_assert(not given_int.implies_subtype_of(T, str))
|
||||
|
||||
given_bool = range_constraint(Never, T, bool)
|
||||
static_assert(is_subtype_of_given(given_bool, T, int))
|
||||
static_assert(is_subtype_of_given(given_bool, T, bool))
|
||||
static_assert(not is_subtype_of_given(given_bool, T, str))
|
||||
given_bool = ConstraintSet.range(Never, T, bool)
|
||||
static_assert(given_bool.implies_subtype_of(T, int))
|
||||
static_assert(given_bool.implies_subtype_of(T, bool))
|
||||
static_assert(not given_bool.implies_subtype_of(T, str))
|
||||
|
||||
given_both = given_bool & given_int
|
||||
static_assert(is_subtype_of_given(given_both, T, int))
|
||||
static_assert(is_subtype_of_given(given_both, T, bool))
|
||||
static_assert(not is_subtype_of_given(given_both, T, str))
|
||||
static_assert(given_both.implies_subtype_of(T, int))
|
||||
static_assert(given_both.implies_subtype_of(T, bool))
|
||||
static_assert(not given_both.implies_subtype_of(T, str))
|
||||
|
||||
given_str = range_constraint(Never, T, str)
|
||||
static_assert(not is_subtype_of_given(given_str, T, int))
|
||||
static_assert(not is_subtype_of_given(given_str, T, bool))
|
||||
static_assert(is_subtype_of_given(given_str, T, str))
|
||||
given_str = ConstraintSet.range(Never, T, str)
|
||||
static_assert(not given_str.implies_subtype_of(T, int))
|
||||
static_assert(not given_str.implies_subtype_of(T, bool))
|
||||
static_assert(given_str.implies_subtype_of(T, str))
|
||||
```
|
||||
|
||||
This might require propagating constraints from other typevars.
|
||||
|
|
@ -179,20 +179,20 @@ This might require propagating constraints from other typevars.
|
|||
```py
|
||||
def mutually_constrained[T, U]():
|
||||
# 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
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of_given(given_int, T, int))
|
||||
static_assert(not is_subtype_of_given(given_int, T, bool))
|
||||
static_assert(not is_subtype_of_given(given_int, T, str))
|
||||
static_assert(given_int.implies_subtype_of(T, int))
|
||||
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||
static_assert(not given_int.implies_subtype_of(T, str))
|
||||
|
||||
# If [T ≤ U ∧ U ≤ int], then [T ≤ int] must be true as well.
|
||||
given_int = 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
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of_given(given_int, T, int))
|
||||
static_assert(not is_subtype_of_given(given_int, T, bool))
|
||||
static_assert(not is_subtype_of_given(given_int, T, str))
|
||||
static_assert(given_int.implies_subtype_of(T, int))
|
||||
static_assert(not given_int.implies_subtype_of(T, bool))
|
||||
static_assert(not given_int.implies_subtype_of(T, str))
|
||||
```
|
||||
|
||||
[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()
|
||||
}
|
||||
|
||||
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)
|
||||
if name == "__get__" && class.is_known(db, KnownClass::FunctionType) =>
|
||||
{
|
||||
|
|
@ -4833,51 +4866,6 @@ impl<'db> Type<'db> {
|
|||
)
|
||||
.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) => {
|
||||
Binding::single(
|
||||
self,
|
||||
|
|
@ -6918,7 +6906,14 @@ impl<'db> Type<'db> {
|
|||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| 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::DataclassTransformer(_)
|
||||
// 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::WrapperDescriptor(_)
|
||||
| Type::KnownBoundMethod(
|
||||
KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen,
|
||||
KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
)
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
|
|
@ -10318,6 +10318,12 @@ pub enum KnownBoundMethodType<'db> {
|
|||
StrStartswith(StringLiteralType<'db>),
|
||||
/// Method wrapper for `Path.open`,
|
||||
PathOpen,
|
||||
|
||||
// ConstraintSet methods
|
||||
ConstraintSetRange,
|
||||
ConstraintSetAlways,
|
||||
ConstraintSetNever,
|
||||
ConstraintSetImpliesSubtypeOf(TrackedConstraintSet<'db>),
|
||||
}
|
||||
|
||||
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) => {
|
||||
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)
|
||||
}
|
||||
|
||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen) => {
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen)
|
||||
| (
|
||||
KnownBoundMethodType::ConstraintSetRange,
|
||||
KnownBoundMethodType::ConstraintSetRange,
|
||||
)
|
||||
| (
|
||||
KnownBoundMethodType::ConstraintSetAlways,
|
||||
KnownBoundMethodType::ConstraintSetAlways,
|
||||
)
|
||||
| (
|
||||
KnownBoundMethodType::ConstraintSetNever,
|
||||
KnownBoundMethodType::ConstraintSetNever,
|
||||
)
|
||||
| (
|
||||
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
) => ConstraintSet::from(true),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||
|
|
@ -10403,13 +10427,21 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen,
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||
| KnownBoundMethodType::FunctionTypeDunderCall(_)
|
||||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen,
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
) => ConstraintSet::from(false),
|
||||
}
|
||||
}
|
||||
|
|
@ -10445,9 +10477,26 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
ConstraintSet::from(self == other)
|
||||
}
|
||||
|
||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen) => {
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen)
|
||||
| (
|
||||
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(_)
|
||||
|
|
@ -10455,13 +10504,21 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen,
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||
| KnownBoundMethodType::FunctionTypeDunderCall(_)
|
||||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen,
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
|
||||
) => ConstraintSet::from(false),
|
||||
}
|
||||
}
|
||||
|
|
@ -10480,7 +10537,12 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
KnownBoundMethodType::PropertyDunderSet(property) => {
|
||||
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::StrStartswith(_) => KnownClass::BuiltinFunctionType,
|
||||
KnownBoundMethodType::PathOpen => KnownClass::MethodType,
|
||||
KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => KnownClass::ConstraintSet,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10592,6 +10658,45 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
KnownBoundMethodType::PathOpen => {
|
||||
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) => {
|
||||
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
|
||||
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) {
|
||||
Some(KnownClass::Bool) => match overload.parameter_types() {
|
||||
[Some(arg)] => overload.set_return_type(arg.bool(db).into_type(db)),
|
||||
|
|
|
|||
|
|
@ -295,6 +295,12 @@ impl<'db> ConstraintSet<'db> {
|
|||
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(
|
||||
db: &'db dyn Db,
|
||||
lower: Type<'db>,
|
||||
|
|
@ -304,15 +310,6 @@ impl<'db> ConstraintSet<'db> {
|
|||
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 {
|
||||
self.node.simplify(db).display(db)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -523,6 +523,18 @@ impl Display for DisplayRepresentation<'_> {
|
|||
Type::KnownBoundMethod(KnownBoundMethodType::PathOpen) => {
|
||||
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) => {
|
||||
let (method, object) = match kind {
|
||||
WrapperDescriptorKind::FunctionTypeDunderGet => ("__get__", "function"),
|
||||
|
|
|
|||
|
|
@ -81,9 +81,9 @@ use crate::types::visitor::any_over_type;
|
|||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase,
|
||||
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
|
||||
NormalizedVisitor, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext,
|
||||
TypeMapping, TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, NormalizedVisitor,
|
||||
SpecialFormType, Truthiness, Type, TypeContext, TypeMapping, TypeRelation, UnionBuilder,
|
||||
binding_type, todo_type, walk_signature,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
|
@ -1299,8 +1299,6 @@ pub enum KnownFunction {
|
|||
IsEquivalentTo,
|
||||
/// `ty_extensions.is_subtype_of`
|
||||
IsSubtypeOf,
|
||||
/// `ty_extensions.is_subtype_of_given`
|
||||
IsSubtypeOfGiven,
|
||||
/// `ty_extensions.is_assignable_to`
|
||||
IsAssignableTo,
|
||||
/// `ty_extensions.is_disjoint_from`
|
||||
|
|
@ -1323,10 +1321,6 @@ pub enum KnownFunction {
|
|||
RevealProtocolInterface,
|
||||
/// `ty_extensions.reveal_mro`
|
||||
RevealMro,
|
||||
/// `ty_extensions.range_constraint`
|
||||
RangeConstraint,
|
||||
/// `ty_extensions.negated_range_constraint`
|
||||
NegatedRangeConstraint,
|
||||
}
|
||||
|
||||
impl KnownFunction {
|
||||
|
|
@ -1393,15 +1387,12 @@ impl KnownFunction {
|
|||
| Self::IsSingleValued
|
||||
| Self::IsSingleton
|
||||
| Self::IsSubtypeOf
|
||||
| Self::IsSubtypeOfGiven
|
||||
| Self::GenericContext
|
||||
| Self::DunderAllNames
|
||||
| Self::EnumMembers
|
||||
| Self::StaticAssert
|
||||
| Self::HasMember
|
||||
| Self::RevealProtocolInterface
|
||||
| Self::RangeConstraint
|
||||
| Self::NegatedRangeConstraint
|
||||
| Self::RevealMro
|
||||
| Self::AllMembers => module.is_ty_extensions(),
|
||||
Self::ImportModule => module.is_importlib(),
|
||||
|
|
@ -1780,32 +1771,6 @@ impl KnownFunction {
|
|||
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 => {
|
||||
// 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.
|
||||
|
|
@ -1894,7 +1859,6 @@ pub(crate) mod tests {
|
|||
|
||||
KnownFunction::IsSingleton
|
||||
| KnownFunction::IsSubtypeOf
|
||||
| KnownFunction::IsSubtypeOfGiven
|
||||
| KnownFunction::GenericContext
|
||||
| KnownFunction::DunderAllNames
|
||||
| KnownFunction::EnumMembers
|
||||
|
|
@ -1905,8 +1869,6 @@ pub(crate) mod tests {
|
|||
| KnownFunction::IsEquivalentTo
|
||||
| KnownFunction::HasMember
|
||||
| KnownFunction::RevealProtocolInterface
|
||||
| KnownFunction::RangeConstraint
|
||||
| KnownFunction::NegatedRangeConstraint
|
||||
| KnownFunction::RevealMro
|
||||
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,29 @@ type JustComplex = TypeOf[1.0j]
|
|||
|
||||
# Constraints
|
||||
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 __eq__(self, other: ConstraintSet) -> bool: ...
|
||||
def __ne__(self, other: ConstraintSet) -> bool: ...
|
||||
|
|
@ -51,13 +74,6 @@ class ConstraintSet:
|
|||
def __or__(self, other: ConstraintSet) -> 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
|
||||
#
|
||||
# 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
|
||||
"""
|
||||
|
||||
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:
|
||||
"""Returns a constraint set that is satisfied when `ty` is `assignable`_ to `to`.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue