Compare commits
11 Commits
alex/subsc
...
dcreager/u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a448e1623 | ||
|
|
336d01957d | ||
|
|
1edae38adf | ||
|
|
ba7f4c4364 | ||
|
|
10eeb9dfda | ||
|
|
5fd909b61e | ||
|
|
7ae7d5db34 | ||
|
|
443586e9cd | ||
|
|
31c9f7059a | ||
|
|
935abab07e | ||
|
|
b2d1475e65 |
@@ -145,7 +145,7 @@ def unbounded_unconstrained[T, U](t: T, u: U) -> None:
|
||||
reveal_type(is_assignable_to(T, object))
|
||||
static_assert(is_assignable_to(T, object))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@unbounded_unconstrained ≤ Super)]
|
||||
reveal_type(is_assignable_to(T, Super))
|
||||
static_assert(not is_assignable_to(T, Super))
|
||||
|
||||
@@ -165,15 +165,15 @@ def unbounded_unconstrained[T, U](t: T, u: U) -> None:
|
||||
reveal_type(is_assignable_to(U, object))
|
||||
static_assert(is_assignable_to(U, object))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@unbounded_unconstrained ≤ Super)]
|
||||
reveal_type(is_assignable_to(U, Super))
|
||||
static_assert(not is_assignable_to(U, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@unbounded_unconstrained ≤ U@unbounded_unconstrained)]
|
||||
reveal_type(is_assignable_to(T, U))
|
||||
static_assert(not is_assignable_to(T, U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@unbounded_unconstrained ≤ T@unbounded_unconstrained)]
|
||||
reveal_type(is_assignable_to(U, T))
|
||||
static_assert(not is_assignable_to(U, T))
|
||||
|
||||
@@ -185,15 +185,15 @@ def unbounded_unconstrained[T, U](t: T, u: U) -> None:
|
||||
reveal_type(is_subtype_of(T, object))
|
||||
static_assert(is_subtype_of(T, object))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@unbounded_unconstrained ≤ Super)]
|
||||
reveal_type(is_subtype_of(T, Super))
|
||||
static_assert(not is_subtype_of(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@unbounded_unconstrained = Never)]
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
static_assert(not is_subtype_of(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@unbounded_unconstrained = object)]
|
||||
reveal_type(is_subtype_of(Any, T))
|
||||
static_assert(not is_subtype_of(Any, T))
|
||||
|
||||
@@ -205,15 +205,15 @@ def unbounded_unconstrained[T, U](t: T, u: U) -> None:
|
||||
reveal_type(is_subtype_of(U, object))
|
||||
static_assert(is_subtype_of(U, object))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@unbounded_unconstrained ≤ Super)]
|
||||
reveal_type(is_subtype_of(U, Super))
|
||||
static_assert(not is_subtype_of(U, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@unbounded_unconstrained ≤ U@unbounded_unconstrained)]
|
||||
reveal_type(is_subtype_of(T, U))
|
||||
static_assert(not is_subtype_of(T, U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@unbounded_unconstrained ≤ T@unbounded_unconstrained)]
|
||||
reveal_type(is_subtype_of(U, T))
|
||||
static_assert(not is_subtype_of(U, T))
|
||||
```
|
||||
@@ -229,31 +229,31 @@ from typing import Any
|
||||
from typing_extensions import final
|
||||
|
||||
def bounded[T: Super](t: T) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded ≤ Super)]
|
||||
reveal_type(is_assignable_to(T, Any))
|
||||
static_assert(is_assignable_to(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded ≤ Super)]
|
||||
reveal_type(is_assignable_to(Any, T))
|
||||
static_assert(is_assignable_to(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded ≤ Super)]
|
||||
reveal_type(is_assignable_to(T, Super))
|
||||
static_assert(is_assignable_to(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded ≤ Sub)]
|
||||
reveal_type(is_assignable_to(T, Sub))
|
||||
static_assert(not is_assignable_to(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded = Super)]
|
||||
reveal_type(is_assignable_to(Super, T))
|
||||
static_assert(not is_assignable_to(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@bounded ≤ Super)]
|
||||
reveal_type(is_assignable_to(Sub, T))
|
||||
static_assert(not is_assignable_to(Sub, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded = Never)]
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
static_assert(not is_subtype_of(T, Any))
|
||||
|
||||
@@ -261,19 +261,19 @@ def bounded[T: Super](t: T) -> None:
|
||||
reveal_type(is_subtype_of(Any, T))
|
||||
static_assert(not is_subtype_of(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded ≤ Super)]
|
||||
reveal_type(is_subtype_of(T, Super))
|
||||
static_assert(is_subtype_of(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded ≤ Sub)]
|
||||
reveal_type(is_subtype_of(T, Sub))
|
||||
static_assert(not is_subtype_of(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded = Super)]
|
||||
reveal_type(is_subtype_of(Super, T))
|
||||
static_assert(not is_subtype_of(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@bounded ≤ Super)]
|
||||
reveal_type(is_subtype_of(Sub, T))
|
||||
static_assert(not is_subtype_of(Sub, T))
|
||||
|
||||
@@ -286,43 +286,43 @@ def bounded_by_gradual[T: Any](t: T) -> None:
|
||||
reveal_type(is_assignable_to(Any, T))
|
||||
static_assert(is_assignable_to(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_by_gradual ≤ Super)]
|
||||
reveal_type(is_assignable_to(T, Super))
|
||||
static_assert(is_assignable_to(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super ≤ T@bounded_by_gradual)]
|
||||
reveal_type(is_assignable_to(Super, T))
|
||||
static_assert(not is_assignable_to(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_by_gradual ≤ Sub)]
|
||||
reveal_type(is_assignable_to(T, Sub))
|
||||
static_assert(is_assignable_to(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@bounded_by_gradual)]
|
||||
reveal_type(is_assignable_to(Sub, T))
|
||||
static_assert(not is_assignable_to(Sub, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_by_gradual = Never)]
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
static_assert(not is_subtype_of(T, Any))
|
||||
static_assert(is_subtype_of(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_by_gradual = object)]
|
||||
reveal_type(is_subtype_of(Any, T))
|
||||
static_assert(not is_subtype_of(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_by_gradual ≤ Super)]
|
||||
reveal_type(is_subtype_of(T, Super))
|
||||
static_assert(not is_subtype_of(T, Super))
|
||||
static_assert(is_subtype_of(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super ≤ T@bounded_by_gradual)]
|
||||
reveal_type(is_subtype_of(Super, T))
|
||||
static_assert(not is_subtype_of(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_by_gradual ≤ Sub)]
|
||||
reveal_type(is_subtype_of(T, Sub))
|
||||
static_assert(not is_subtype_of(T, Sub))
|
||||
static_assert(is_subtype_of(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Sub ≤ T@bounded_by_gradual)]
|
||||
reveal_type(is_subtype_of(Sub, T))
|
||||
static_assert(not is_subtype_of(Sub, T))
|
||||
|
||||
@@ -330,23 +330,23 @@ def bounded_by_gradual[T: Any](t: T) -> None:
|
||||
class FinalClass: ...
|
||||
|
||||
def bounded_final[T: FinalClass](t: T) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final ≤ FinalClass)]
|
||||
reveal_type(is_assignable_to(T, Any))
|
||||
static_assert(is_assignable_to(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final ≤ FinalClass)]
|
||||
reveal_type(is_assignable_to(Any, T))
|
||||
static_assert(is_assignable_to(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final ≤ FinalClass)]
|
||||
reveal_type(is_assignable_to(T, FinalClass))
|
||||
static_assert(is_assignable_to(T, FinalClass))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final = FinalClass)]
|
||||
reveal_type(is_assignable_to(FinalClass, T))
|
||||
static_assert(not is_assignable_to(FinalClass, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final = Never)]
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
static_assert(not is_subtype_of(T, Any))
|
||||
|
||||
@@ -354,11 +354,11 @@ def bounded_final[T: FinalClass](t: T) -> None:
|
||||
reveal_type(is_subtype_of(Any, T))
|
||||
static_assert(not is_subtype_of(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final ≤ FinalClass)]
|
||||
reveal_type(is_subtype_of(T, FinalClass))
|
||||
static_assert(is_subtype_of(T, FinalClass))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@bounded_final = FinalClass)]
|
||||
reveal_type(is_subtype_of(FinalClass, T))
|
||||
static_assert(not is_subtype_of(FinalClass, T))
|
||||
```
|
||||
@@ -370,36 +370,36 @@ typevars to `Never` in addition to that final class.
|
||||
|
||||
```py
|
||||
def two_bounded[T: Super, U: Super](t: T, u: U) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((T@two_bounded ≤ U@two_bounded) ∧ (U@two_bounded ≤ Super))]
|
||||
reveal_type(is_assignable_to(T, U))
|
||||
static_assert(not is_assignable_to(T, U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((U@two_bounded ≤ Super) ∧ (U@two_bounded ≤ T@two_bounded ≤ Super))]
|
||||
reveal_type(is_assignable_to(U, T))
|
||||
static_assert(not is_assignable_to(U, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((T@two_bounded ≤ U@two_bounded) ∧ (U@two_bounded ≤ Super))]
|
||||
reveal_type(is_subtype_of(T, U))
|
||||
static_assert(not is_subtype_of(T, U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((U@two_bounded ≤ Super) ∧ (U@two_bounded ≤ T@two_bounded ≤ Super))]
|
||||
reveal_type(is_subtype_of(U, T))
|
||||
static_assert(not is_subtype_of(U, T))
|
||||
|
||||
def two_final_bounded[T: FinalClass, U: FinalClass](t: T, u: U) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((T@two_final_bounded ≤ U@two_final_bounded) ∧ (U@two_final_bounded ≤ FinalClass))]
|
||||
reveal_type(is_assignable_to(T, U))
|
||||
static_assert(not is_assignable_to(T, U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((U@two_final_bounded ≤ FinalClass) ∧ (U@two_final_bounded ≤ T@two_final_bounded ≤ FinalClass))]
|
||||
reveal_type(is_assignable_to(U, T))
|
||||
static_assert(not is_assignable_to(U, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((T@two_final_bounded ≤ U@two_final_bounded) ∧ (U@two_final_bounded ≤ FinalClass))]
|
||||
reveal_type(is_subtype_of(T, U))
|
||||
static_assert(not is_subtype_of(T, U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[((U@two_final_bounded ≤ FinalClass) ∧ (U@two_final_bounded ≤ T@two_final_bounded ≤ FinalClass))]
|
||||
reveal_type(is_subtype_of(U, T))
|
||||
static_assert(not is_subtype_of(U, T))
|
||||
```
|
||||
@@ -412,11 +412,11 @@ intersection of all of its constraints is a subtype of the typevar.
|
||||
from ty_extensions import Intersection
|
||||
|
||||
def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base)]
|
||||
reveal_type(is_assignable_to(T, Super))
|
||||
static_assert(not is_assignable_to(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base)]
|
||||
reveal_type(is_assignable_to(T, Base))
|
||||
static_assert(not is_assignable_to(T, Base))
|
||||
|
||||
@@ -424,27 +424,27 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_assignable_to(T, Sub))
|
||||
static_assert(not is_assignable_to(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Unrelated))
|
||||
static_assert(not is_assignable_to(T, Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Any))
|
||||
static_assert(is_assignable_to(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Super | Unrelated))
|
||||
static_assert(is_assignable_to(T, Super | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Base | Unrelated))
|
||||
static_assert(is_assignable_to(T, Base | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Sub | Unrelated))
|
||||
static_assert(not is_assignable_to(T, Sub | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(Any, T))
|
||||
static_assert(is_assignable_to(Any, T))
|
||||
|
||||
@@ -452,7 +452,7 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_assignable_to(Super, T))
|
||||
static_assert(not is_assignable_to(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(Unrelated, T))
|
||||
static_assert(not is_assignable_to(Unrelated, T))
|
||||
|
||||
@@ -460,15 +460,15 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_assignable_to(Super | Unrelated, T))
|
||||
static_assert(not is_assignable_to(Super | Unrelated, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_assignable_to(Intersection[Base, Unrelated], T))
|
||||
static_assert(is_assignable_to(Intersection[Base, Unrelated], T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base)]
|
||||
reveal_type(is_subtype_of(T, Super))
|
||||
static_assert(not is_subtype_of(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base)]
|
||||
reveal_type(is_subtype_of(T, Base))
|
||||
static_assert(not is_subtype_of(T, Base))
|
||||
|
||||
@@ -476,7 +476,7 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_subtype_of(T, Sub))
|
||||
static_assert(not is_subtype_of(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Unrelated)]
|
||||
reveal_type(is_subtype_of(T, Unrelated))
|
||||
static_assert(not is_subtype_of(T, Unrelated))
|
||||
|
||||
@@ -484,15 +484,15 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
static_assert(not is_subtype_of(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_subtype_of(T, Super | Unrelated))
|
||||
static_assert(is_subtype_of(T, Super | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_subtype_of(T, Base | Unrelated))
|
||||
static_assert(is_subtype_of(T, Base | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Unrelated)]
|
||||
reveal_type(is_subtype_of(T, Sub | Unrelated))
|
||||
static_assert(not is_subtype_of(T, Sub | Unrelated))
|
||||
|
||||
@@ -504,7 +504,7 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_subtype_of(Super, T))
|
||||
static_assert(not is_subtype_of(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Unrelated)]
|
||||
reveal_type(is_subtype_of(Unrelated, T))
|
||||
static_assert(not is_subtype_of(Unrelated, T))
|
||||
|
||||
@@ -512,24 +512,24 @@ def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||
reveal_type(is_subtype_of(Super | Unrelated, T))
|
||||
static_assert(not is_subtype_of(Super | Unrelated, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained = Base) ∨ (T@constrained = Unrelated)]
|
||||
reveal_type(is_subtype_of(Intersection[Base, Unrelated], T))
|
||||
static_assert(is_subtype_of(Intersection[Base, Unrelated], T))
|
||||
|
||||
def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Super)]
|
||||
reveal_type(is_assignable_to(T, Super))
|
||||
static_assert(is_assignable_to(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Base)]
|
||||
reveal_type(is_assignable_to(T, Base))
|
||||
static_assert(is_assignable_to(T, Base))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Sub)]
|
||||
reveal_type(is_assignable_to(T, Sub))
|
||||
static_assert(not is_assignable_to(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Unrelated))
|
||||
static_assert(not is_assignable_to(T, Unrelated))
|
||||
|
||||
@@ -541,19 +541,19 @@ def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
|
||||
reveal_type(is_assignable_to(T, Super | Any))
|
||||
static_assert(is_assignable_to(T, Super | Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Super | Unrelated)]
|
||||
reveal_type(is_assignable_to(T, Super | Unrelated))
|
||||
static_assert(is_assignable_to(T, Super | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Super, T))
|
||||
static_assert(not is_assignable_to(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Base, T))
|
||||
static_assert(is_assignable_to(Base, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Unrelated ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Unrelated, T))
|
||||
static_assert(not is_assignable_to(Unrelated, T))
|
||||
|
||||
@@ -561,19 +561,19 @@ def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
|
||||
reveal_type(is_assignable_to(Any, T))
|
||||
static_assert(is_assignable_to(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Super | Any, T))
|
||||
static_assert(not is_assignable_to(Super | Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Base | Any, T))
|
||||
static_assert(is_assignable_to(Base | Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super | Unrelated ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Super | Unrelated, T))
|
||||
static_assert(not is_assignable_to(Super | Unrelated, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(Base & Unrelated ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_assignable_to(Intersection[Base, Unrelated], T))
|
||||
static_assert(is_assignable_to(Intersection[Base, Unrelated], T))
|
||||
|
||||
@@ -581,69 +581,69 @@ def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
|
||||
reveal_type(is_assignable_to(Intersection[Base, Any], T))
|
||||
static_assert(is_assignable_to(Intersection[Base, Any], T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Super)]
|
||||
reveal_type(is_subtype_of(T, Super))
|
||||
static_assert(not is_subtype_of(T, Super))
|
||||
static_assert(is_subtype_of(T, Super))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Base)]
|
||||
reveal_type(is_subtype_of(T, Base))
|
||||
static_assert(not is_subtype_of(T, Base))
|
||||
static_assert(is_subtype_of(T, Base))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Sub)]
|
||||
reveal_type(is_subtype_of(T, Sub))
|
||||
static_assert(not is_subtype_of(T, Sub))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Unrelated)]
|
||||
reveal_type(is_subtype_of(T, Unrelated))
|
||||
static_assert(not is_subtype_of(T, Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual = Never)]
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
static_assert(not is_subtype_of(T, Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Super)]
|
||||
reveal_type(is_subtype_of(T, Super | Any))
|
||||
static_assert(not is_subtype_of(T, Super | Any))
|
||||
static_assert(is_subtype_of(T, Super | Any))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual ≤ Super | Unrelated)]
|
||||
reveal_type(is_subtype_of(T, Super | Unrelated))
|
||||
static_assert(not is_subtype_of(T, Super | Unrelated))
|
||||
static_assert(is_subtype_of(T, Super | Unrelated))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_subtype_of(Super, T))
|
||||
static_assert(not is_subtype_of(Super, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_subtype_of(Base, T))
|
||||
static_assert(not is_subtype_of(Base, T))
|
||||
static_assert(is_subtype_of(Base, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Unrelated ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_subtype_of(Unrelated, T))
|
||||
static_assert(not is_subtype_of(Unrelated, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual = object)]
|
||||
reveal_type(is_subtype_of(Any, T))
|
||||
static_assert(not is_subtype_of(Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual = object)]
|
||||
reveal_type(is_subtype_of(Super | Any, T))
|
||||
static_assert(not is_subtype_of(Super | Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@constrained_by_gradual = object)]
|
||||
reveal_type(is_subtype_of(Base | Any, T))
|
||||
static_assert(not is_subtype_of(Base | Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Super | Unrelated ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_subtype_of(Super | Unrelated, T))
|
||||
static_assert(not is_subtype_of(Super | Unrelated, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Base & Unrelated ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_subtype_of(Intersection[Base, Unrelated], T))
|
||||
static_assert(not is_subtype_of(Intersection[Base, Unrelated], T))
|
||||
static_assert(is_subtype_of(Intersection[Base, Unrelated], T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@constrained_by_gradual)]
|
||||
reveal_type(is_subtype_of(Intersection[Base, Any], T))
|
||||
static_assert(not is_subtype_of(Intersection[Base, Any], T))
|
||||
static_assert(is_subtype_of(Intersection[Base, Any], T))
|
||||
```
|
||||
|
||||
Two distinct fully static typevars are not subtypes of each other, even if they have the same
|
||||
@@ -694,19 +694,19 @@ A bound or constrained typevar is a subtype of itself in a union:
|
||||
|
||||
```py
|
||||
def union[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@union ≤ Base)]
|
||||
reveal_type(is_assignable_to(T, T | None))
|
||||
static_assert(is_assignable_to(T, T | None))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@union = Base) ∨ (U@union = Unrelated)]
|
||||
reveal_type(is_assignable_to(U, U | None))
|
||||
static_assert(is_assignable_to(U, U | None))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@union ≤ Base)]
|
||||
reveal_type(is_subtype_of(T, T | None))
|
||||
static_assert(is_subtype_of(T, T | None))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@union = Base) ∨ (U@union = Unrelated)]
|
||||
reveal_type(is_subtype_of(U, U | None))
|
||||
static_assert(is_subtype_of(U, U | None))
|
||||
```
|
||||
@@ -715,11 +715,11 @@ A bound or constrained typevar in a union with a dynamic type is assignable to t
|
||||
|
||||
```py
|
||||
def union_with_dynamic[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@union_with_dynamic ≤ Base)]
|
||||
reveal_type(is_assignable_to(T | Any, T))
|
||||
static_assert(is_assignable_to(T | Any, T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@union_with_dynamic = Base) ∨ (U@union_with_dynamic = Unrelated)]
|
||||
reveal_type(is_assignable_to(U | Any, U))
|
||||
static_assert(is_assignable_to(U | Any, U))
|
||||
|
||||
@@ -740,19 +740,19 @@ from ty_extensions import Intersection, Not, is_disjoint_from
|
||||
class A: ...
|
||||
|
||||
def inter[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@inter ≤ Base)]
|
||||
reveal_type(is_assignable_to(Intersection[T, Unrelated], T))
|
||||
static_assert(is_assignable_to(Intersection[T, Unrelated], T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@inter ≤ Base)]
|
||||
reveal_type(is_subtype_of(Intersection[T, Unrelated], T))
|
||||
static_assert(is_subtype_of(Intersection[T, Unrelated], T))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@inter = Base) ∨ (U@inter = Unrelated)]
|
||||
reveal_type(is_assignable_to(Intersection[U, A], U))
|
||||
static_assert(is_assignable_to(Intersection[U, A], U))
|
||||
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
# revealed: ty_extensions.ConstraintSet[(U@inter = Base) ∨ (U@inter = Unrelated)]
|
||||
reveal_type(is_subtype_of(Intersection[U, A], U))
|
||||
static_assert(is_subtype_of(Intersection[U, A], U))
|
||||
|
||||
|
||||
@@ -45,29 +45,25 @@ def even_given_unsatisfiable_constraints():
|
||||
|
||||
## Type variables
|
||||
|
||||
The interesting case is typevars. The other typing relationships (TODO: will) all "punt" on the
|
||||
question when considering a typevar, by translating the desired relationship into a constraint set.
|
||||
The interesting case is typevars. The other typing relationships all "punt" on the question when
|
||||
considering a typevar, by translating the desired relationship into a constraint set.
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
from ty_extensions import is_assignable_to, is_subtype_of
|
||||
|
||||
def assignability[T]():
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@assignability ≤ bool]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@assignability ≤ bool)]
|
||||
reveal_type(is_assignable_to(T, bool))
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@assignability ≤ int]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@assignability ≤ int)]
|
||||
reveal_type(is_assignable_to(T, int))
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
reveal_type(is_assignable_to(T, object))
|
||||
|
||||
def subtyping[T]():
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@subtyping ≤ bool]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@subtyping ≤ bool)]
|
||||
reveal_type(is_subtype_of(T, bool))
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@subtyping ≤ int]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@subtyping ≤ int)]
|
||||
reveal_type(is_subtype_of(T, int))
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
reveal_type(is_subtype_of(T, object))
|
||||
@@ -88,49 +84,37 @@ class Contravariant[T]:
|
||||
pass
|
||||
|
||||
def assignability[T]():
|
||||
# aka [T@assignability ≤ object], which is always satisfiable
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
reveal_type(is_assignable_to(T, Any))
|
||||
|
||||
# aka [Never ≤ T@assignability], which is always satisfiable
|
||||
# revealed: ty_extensions.ConstraintSet[always]
|
||||
reveal_type(is_assignable_to(Any, T))
|
||||
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@assignability ≤ Covariant[object]]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@assignability ≤ Covariant[object])]
|
||||
reveal_type(is_assignable_to(T, Covariant[Any]))
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[Covariant[Never] ≤ T@assignability]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Covariant[Never] ≤ T@assignability)]
|
||||
reveal_type(is_assignable_to(Covariant[Any], T))
|
||||
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@assignability ≤ Contravariant[Never]]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@assignability ≤ Contravariant[Never])]
|
||||
reveal_type(is_assignable_to(T, Contravariant[Any]))
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[Contravariant[object] ≤ T@assignability]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Contravariant[object] ≤ T@assignability)]
|
||||
reveal_type(is_assignable_to(Contravariant[Any], T))
|
||||
|
||||
def subtyping[T]():
|
||||
# aka [T@assignability ≤ object], which is always satisfiable
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@subtyping = Never)]
|
||||
reveal_type(is_subtype_of(T, Any))
|
||||
|
||||
# aka [Never ≤ T@assignability], which is always satisfiable
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@subtyping = object)]
|
||||
reveal_type(is_subtype_of(Any, T))
|
||||
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@subtyping ≤ Covariant[Never]]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@subtyping ≤ Covariant[Never])]
|
||||
reveal_type(is_subtype_of(T, Covariant[Any]))
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[Covariant[object] ≤ T@subtyping]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Covariant[object] ≤ T@subtyping)]
|
||||
reveal_type(is_subtype_of(Covariant[Any], T))
|
||||
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[T@subtyping ≤ Contravariant[object]]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(T@subtyping ≤ Contravariant[object])]
|
||||
reveal_type(is_subtype_of(T, Contravariant[Any]))
|
||||
# TODO: revealed: ty_extensions.ConstraintSet[Contravariant[Never] ≤ T@subtyping]
|
||||
# revealed: ty_extensions.ConstraintSet[never]
|
||||
# revealed: ty_extensions.ConstraintSet[(Contravariant[Never] ≤ T@subtyping)]
|
||||
reveal_type(is_subtype_of(Contravariant[Any], T))
|
||||
```
|
||||
|
||||
|
||||
@@ -1248,14 +1248,10 @@ def identity[T](t: T) -> T:
|
||||
|
||||
static_assert(is_assignable_to(TypeOf[identity], Callable[[int], int]))
|
||||
static_assert(is_assignable_to(TypeOf[identity], Callable[[str], str]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(TypeOf[identity], Callable[[str], int]))
|
||||
|
||||
static_assert(is_assignable_to(CallableTypeOf[identity], Callable[[int], int]))
|
||||
static_assert(is_assignable_to(CallableTypeOf[identity], Callable[[str], str]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(CallableTypeOf[identity], Callable[[str], int]))
|
||||
```
|
||||
|
||||
|
||||
@@ -2221,23 +2221,11 @@ from ty_extensions import CallableTypeOf, TypeOf, is_subtype_of, static_assert
|
||||
def identity[T](t: T) -> T:
|
||||
return t
|
||||
|
||||
# TODO: Confusingly, these are not the same results as the corresponding checks in
|
||||
# is_assignable_to.md, even though all of these types are fully static. We have some heuristics that
|
||||
# currently conflict with each other, that we are in the process of removing with the constraint set
|
||||
# work.
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(TypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(TypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not is_subtype_of(TypeOf[identity], Callable[[str], int]))
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(CallableTypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(CallableTypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not is_subtype_of(CallableTypeOf[identity], Callable[[str], int]))
|
||||
```
|
||||
|
||||
@@ -1310,7 +1310,7 @@ impl<'db> Type<'db> {
|
||||
self.filter_union(db, |elem| {
|
||||
!elem
|
||||
.when_disjoint_from(db, target, inferable)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, inferable)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1625,7 +1625,7 @@ impl<'db> Type<'db> {
|
||||
/// See [`TypeRelation::Subtyping`] for more details.
|
||||
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||
self.when_subtype_of(db, target, InferableTypeVars::None)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
fn when_subtype_of(
|
||||
@@ -1661,7 +1661,7 @@ impl<'db> Type<'db> {
|
||||
/// See [`TypeRelation::Assignability`] for more details.
|
||||
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||
self.when_assignable_to(db, target, InferableTypeVars::None)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
fn when_assignable_to(
|
||||
@@ -1679,7 +1679,7 @@ impl<'db> Type<'db> {
|
||||
#[salsa::tracked(cycle_initial=is_redundant_with_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn is_redundant_with(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||
self.has_relation_to(db, other, InferableTypeVars::None, TypeRelation::Redundancy)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
fn has_relation_to(
|
||||
@@ -1726,6 +1726,29 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
match (self, target) {
|
||||
// Two identical typevars must always solve to the same type, so they are always
|
||||
// subtypes of each other and assignable to each other.
|
||||
//
|
||||
// Note that this is not handled by the early return at the beginning of this method,
|
||||
// since subtyping between a TypeVar and an arbitrary other type cannot be guaranteed to be reflexive.
|
||||
(Type::TypeVar(lhs_bound_typevar), Type::TypeVar(rhs_bound_typevar))
|
||||
if lhs_bound_typevar.is_same_typevar_as(db, rhs_bound_typevar) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
|
||||
// A typevar satisfies a relation when...it satisfies the relation. Yes that's a
|
||||
// tautology! We're moving the caller's subtyping/assignability requirement into a
|
||||
// constraint set. If the typevar has an upper bound or constraints, then the relation
|
||||
// only has to hold when the typevar has a valid specialization (i.e., one that
|
||||
// satisfies the upper bound/constraints).
|
||||
(Type::TypeVar(bound_typevar), _) => {
|
||||
ConstraintSet::constrain_typevar(db, bound_typevar, Type::Never, target, relation)
|
||||
}
|
||||
(_, Type::TypeVar(bound_typevar)) => {
|
||||
ConstraintSet::constrain_typevar(db, bound_typevar, self, Type::object(), relation)
|
||||
}
|
||||
|
||||
// Everything is a subtype of `object`.
|
||||
(_, Type::NominalInstance(instance)) if instance.is_object() => {
|
||||
ConstraintSet::from(true)
|
||||
@@ -1829,130 +1852,6 @@ impl<'db> Type<'db> {
|
||||
},
|
||||
}),
|
||||
|
||||
// In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied:
|
||||
// 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`.
|
||||
// TypeVars without an explicit upper bound are treated as having an implicit upper bound of `object`.
|
||||
// 2. `T` is a constrained TypeVar and all of `T`'s constraints are subtypes of `S`.
|
||||
//
|
||||
// However, there is one exception to this general rule: for any given typevar `T`,
|
||||
// `T` will always be a subtype of any union containing `T`.
|
||||
// A similar rule applies in reverse to intersection types.
|
||||
(Type::TypeVar(bound_typevar), Type::Union(union))
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& union.elements(db).contains(&self) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
(Type::Intersection(intersection), Type::TypeVar(bound_typevar))
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& intersection.positive(db).contains(&target) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
(Type::Intersection(intersection), Type::TypeVar(bound_typevar))
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& intersection.negative(db).contains(&target) =>
|
||||
{
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
// Two identical typevars must always solve to the same type, so they are always
|
||||
// subtypes of each other and assignable to each other.
|
||||
//
|
||||
// Note that this is not handled by the early return at the beginning of this method,
|
||||
// since subtyping between a TypeVar and an arbitrary other type cannot be guaranteed to be reflexive.
|
||||
(Type::TypeVar(lhs_bound_typevar), Type::TypeVar(rhs_bound_typevar))
|
||||
if !lhs_bound_typevar.is_inferable(db, inferable)
|
||||
&& lhs_bound_typevar.is_same_typevar_as(db, rhs_bound_typevar) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
|
||||
// A fully static typevar is a subtype of its upper bound, and to something similar to
|
||||
// the union of its constraints. An unbound, unconstrained, fully static typevar has an
|
||||
// implicit upper bound of `object` (which is handled above).
|
||||
(Type::TypeVar(bound_typevar), _)
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& bound_typevar.typevar(db).bound_or_constraints(db).is_some() =>
|
||||
{
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => unreachable!(),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
target,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
),
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
constraints.elements(db).iter().when_all(db, |constraint| {
|
||||
constraint.has_relation_to_impl(
|
||||
db,
|
||||
target,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the typevar is constrained, there must be multiple constraints, and the typevar
|
||||
// might be specialized to any one of them. However, the constraints do not have to be
|
||||
// disjoint, which means an lhs type might be a subtype of all of the constraints.
|
||||
(_, Type::TypeVar(bound_typevar))
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& !bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.when_some_and(|constraints| {
|
||||
constraints.iter().when_all(db, |constraint| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
*constraint,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
})
|
||||
.is_never_satisfied(db) =>
|
||||
{
|
||||
// TODO: The repetition here isn't great, but we really need the fallthrough logic,
|
||||
// where this arm only engages if it returns true (or in the world of constraints,
|
||||
// not false). Once we're using real constraint sets instead of bool, we should be
|
||||
// able to simplify the typevar logic.
|
||||
bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.when_some_and(|constraints| {
|
||||
constraints.iter().when_all(db, |constraint| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
*constraint,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
(Type::TypeVar(bound_typevar), _) if bound_typevar.is_inferable(db, inferable) => {
|
||||
// The implicit lower bound of a typevar is `Never`, which means
|
||||
// that it is always assignable to any other type.
|
||||
|
||||
// TODO: record the unification constraints
|
||||
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
|
||||
// `Never` is the bottom type, the empty set.
|
||||
(_, Type::Never) => ConstraintSet::from(false),
|
||||
|
||||
@@ -2044,55 +1943,6 @@ impl<'db> Type<'db> {
|
||||
})
|
||||
}
|
||||
|
||||
// Other than the special cases checked above, no other types are a subtype of a
|
||||
// typevar, since there's no guarantee what type the typevar will be specialized to.
|
||||
// (If the typevar is bounded, it might be specialized to a smaller type than the
|
||||
// bound. This is true even if the bound is a final class, since the typevar can still
|
||||
// be specialized to `Never`.)
|
||||
(_, Type::TypeVar(bound_typevar)) if !bound_typevar.is_inferable(db, inferable) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
(_, Type::TypeVar(typevar))
|
||||
if typevar.is_inferable(db, inferable)
|
||||
&& relation.is_assignability()
|
||||
&& typevar.typevar(db).upper_bound(db).is_none_or(|bound| {
|
||||
!self
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
bound,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
.is_never_satisfied(db)
|
||||
}) =>
|
||||
{
|
||||
// TODO: record the unification constraints
|
||||
|
||||
typevar.typevar(db).upper_bound(db).when_none_or(|bound| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
bound,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Infer specializations here
|
||||
(_, Type::TypeVar(bound_typevar)) if bound_typevar.is_inferable(db, inferable) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
(Type::TypeVar(bound_typevar), _) => {
|
||||
// All inferable cases should have been handled above
|
||||
assert!(!bound_typevar.is_inferable(db, inferable));
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
(Type::TypedDict(_), _) => {
|
||||
// TODO: Implement assignability and subtyping for TypedDict
|
||||
ConstraintSet::from(relation.is_assignability())
|
||||
@@ -2564,7 +2414,7 @@ impl<'db> Type<'db> {
|
||||
/// [equivalent to]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent
|
||||
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||
self.when_equivalent_to(db, other, InferableTypeVars::None)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
fn when_equivalent_to(
|
||||
@@ -2691,7 +2541,7 @@ impl<'db> Type<'db> {
|
||||
/// `false` answers in some cases.
|
||||
pub(crate) fn is_disjoint_from(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||
self.when_disjoint_from(db, other, InferableTypeVars::None)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
fn when_disjoint_from(
|
||||
@@ -4957,7 +4807,7 @@ impl<'db> Type<'db> {
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked_set)) => {
|
||||
let constraints = tracked_set.constraints(db);
|
||||
Truthiness::from(constraints.is_always_satisfied(db))
|
||||
Truthiness::from(constraints.satisfied_by_all_typevars(db, InferableTypeVars::None))
|
||||
}
|
||||
|
||||
Type::FunctionLiteral(_)
|
||||
@@ -8211,7 +8061,9 @@ impl<'db> KnownInstanceType<'db> {
|
||||
Ok(())
|
||||
}
|
||||
KnownInstanceType::ConstraintSet(tracked_set) => {
|
||||
let constraints = tracked_set.constraints(self.db);
|
||||
let constraints = tracked_set
|
||||
.constraints(self.db)
|
||||
.limit_to_valid_specializations(self.db);
|
||||
write!(
|
||||
f,
|
||||
"ty_extensions.ConstraintSet[{}]",
|
||||
|
||||
@@ -1658,7 +1658,7 @@ impl<'db> CallableBinding<'db> {
|
||||
.unwrap_or(Type::unknown());
|
||||
if argument_type
|
||||
.when_assignable_to(db, parameter_type, overload.inferable_typevars)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, overload.inferable_typevars)
|
||||
{
|
||||
is_argument_assignable_to_any_overload = true;
|
||||
break 'overload;
|
||||
@@ -1888,7 +1888,7 @@ impl<'db> CallableBinding<'db> {
|
||||
current_parameter_type,
|
||||
overload.inferable_typevars,
|
||||
)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, overload.inferable_typevars)
|
||||
{
|
||||
participating_parameter_indexes.insert(parameter_index);
|
||||
}
|
||||
@@ -2011,7 +2011,7 @@ impl<'db> CallableBinding<'db> {
|
||||
first_overload_return_type,
|
||||
overload.inferable_typevars,
|
||||
)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, overload.inferable_typevars)
|
||||
})
|
||||
} else {
|
||||
// No matching overload
|
||||
@@ -2809,15 +2809,21 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
.zip(self.call_expression_tcx.annotation);
|
||||
|
||||
self.inferable_typevars = generic_context.inferable_typevars(self.db);
|
||||
let valid_specializations = generic_context.valid_specializations(self.db);
|
||||
let mut constraints = ConstraintSet::from(true);
|
||||
let mut builder = SpecializationBuilder::new(self.db, self.inferable_typevars);
|
||||
|
||||
// Prefer the declared type of generic classes.
|
||||
let preferred_type_mappings = return_with_tcx.and_then(|(return_ty, tcx)| {
|
||||
tcx.filter_union(self.db, |ty| ty.class_specialization(self.db).is_some())
|
||||
.class_specialization(self.db)?;
|
||||
|
||||
builder.infer(return_ty, tcx).ok()?;
|
||||
Some(builder.type_mappings().clone())
|
||||
let return_type_mappings =
|
||||
return_ty.when_assignable_to(self.db, tcx, self.inferable_typevars);
|
||||
if return_type_mappings.is_never_satisfied(self.db) {
|
||||
return None;
|
||||
}
|
||||
constraints.intersect(self.db, return_type_mappings);
|
||||
Some(return_type_mappings)
|
||||
});
|
||||
|
||||
// For a given type variable, we track the variance of any assignments to that type variable
|
||||
@@ -2837,22 +2843,11 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
continue;
|
||||
};
|
||||
|
||||
let argument_type = variadic_argument_type.unwrap_or(argument_type);
|
||||
let specialization_result = builder.infer_map(
|
||||
expected_type,
|
||||
variadic_argument_type.unwrap_or(argument_type),
|
||||
argument_type,
|
||||
|(identity, variance, inferred_ty)| {
|
||||
// Avoid widening the inferred type if it is already assignable to the
|
||||
// preferred declared type.
|
||||
if preferred_type_mappings
|
||||
.as_ref()
|
||||
.and_then(|types| types.get(&identity))
|
||||
.is_some_and(|preferred_ty| {
|
||||
inferred_ty.is_assignable_to(self.db, *preferred_ty)
|
||||
})
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
variance_in_arguments
|
||||
.entry(identity)
|
||||
.and_modify(|current| *current = current.join(variance))
|
||||
@@ -2868,8 +2863,44 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
argument_index: adjusted_argument_index,
|
||||
});
|
||||
}
|
||||
|
||||
let argument_constraints = expected_type.when_assignable_to(
|
||||
self.db,
|
||||
variadic_argument_type.unwrap_or(argument_type),
|
||||
self.inferable_typevars,
|
||||
);
|
||||
if argument_constraints.is_never_satisfied(self.db) {
|
||||
// This argument is never assignable to its parameter, without considering any
|
||||
// typevars. This will be caught by `check_argument_types` later.
|
||||
continue;
|
||||
}
|
||||
|
||||
let valid_argument_constraints =
|
||||
argument_constraints.and(self.db, || valid_specializations);
|
||||
if valid_argument_constraints.is_never_satisfied(self.db) {
|
||||
// There are specializations that make this argument assignable to its
|
||||
// parameter, but none of them are _valid_ specializations.
|
||||
// XXX: Figure out which typevars are violated and create a nice
|
||||
// SpecializationError.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid widening the inferred type if it is already assignable to the
|
||||
// preferred declared type.
|
||||
// XXX: Because constraint sets are ANDed together this might not be needed? AND
|
||||
// should prefer the tighter specialization.
|
||||
|
||||
// XXX: Determine typevar variance per argument
|
||||
eprintln!(
|
||||
"--> arg {} {} {}",
|
||||
argument_index,
|
||||
argument_constraints.display(self.db),
|
||||
valid_argument_constraints.display(self.db),
|
||||
);
|
||||
constraints.intersect(self.db, valid_argument_constraints);
|
||||
}
|
||||
}
|
||||
eprintln!("--> constraints {}", constraints.display(self.db));
|
||||
|
||||
// Attempt to promote any literal types assigned to the specialization.
|
||||
let maybe_promote = |identity, typevar, ty: Type<'db>| {
|
||||
@@ -2921,15 +2952,27 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
};
|
||||
|
||||
// Build the specialization first without inferring the complete type context.
|
||||
let isolated_specialization = builder
|
||||
.mapped(generic_context, maybe_promote)
|
||||
.build(generic_context);
|
||||
let Ok(isolated_specialization) =
|
||||
generic_context.specialize_constrained_mapped(self.db, constraints, maybe_promote)
|
||||
else {
|
||||
// XXX: better error
|
||||
eprintln!("--> X1");
|
||||
return;
|
||||
};
|
||||
// XXX: maybe_promote
|
||||
let isolated_return_ty = self
|
||||
.return_ty
|
||||
.apply_specialization(self.db, isolated_specialization);
|
||||
eprintln!("--> spec {}", isolated_specialization.display_full(self.db));
|
||||
eprintln!("--> rty {}", isolated_return_ty.display(self.db));
|
||||
|
||||
let mut try_infer_tcx = || {
|
||||
let (return_ty, call_expression_tcx) = return_with_tcx?;
|
||||
eprintln!(
|
||||
"--> infer tcx {} {}",
|
||||
return_ty.display(self.db),
|
||||
call_expression_tcx.display(self.db)
|
||||
);
|
||||
|
||||
// A type variable is not a useful type-context for expression inference, and applying it
|
||||
// to the return type can lead to confusing unions in nested generic calls.
|
||||
@@ -2940,17 +2983,31 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
// If the return type is already assignable to the annotated type, we ignore the rest of
|
||||
// the type context and prefer the narrower inferred type.
|
||||
if isolated_return_ty.is_assignable_to(self.db, call_expression_tcx) {
|
||||
eprintln!("--> X2");
|
||||
return None;
|
||||
}
|
||||
|
||||
// TODO: Ideally we would infer the annotated type _before_ the arguments if this call is part of an
|
||||
// annotated assignment, to closer match the order of any unions written in the type annotation.
|
||||
builder.infer(return_ty, call_expression_tcx).ok()?;
|
||||
let return_constraints = return_ty
|
||||
.when_assignable_to(self.db, call_expression_tcx, self.inferable_typevars)
|
||||
.and(self.db, || valid_specializations);
|
||||
if return_constraints.is_never_satisfied(self.db) {
|
||||
eprintln!("--> X3");
|
||||
return None;
|
||||
}
|
||||
|
||||
// Otherwise, build the specialization again after inferring the complete type context.
|
||||
let specialization = builder
|
||||
.mapped(generic_context, maybe_promote)
|
||||
.build(generic_context);
|
||||
let Ok(specialization) = generic_context.specialize_constrained_mapped(
|
||||
self.db,
|
||||
constraints.and(self.db, || return_constraints),
|
||||
maybe_promote,
|
||||
) else {
|
||||
// XXX: better return
|
||||
eprintln!("--> X4");
|
||||
return None;
|
||||
};
|
||||
// XXX: maybe_promote
|
||||
let return_ty = return_ty.apply_specialization(self.db, specialization);
|
||||
|
||||
Some((Some(specialization), return_ty))
|
||||
@@ -2970,19 +3027,40 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
let parameters = self.signature.parameters();
|
||||
let parameter = ¶meters[parameter_index];
|
||||
if let Some(mut expected_ty) = parameter.annotated_type() {
|
||||
eprintln!(
|
||||
"--> before {:?} {} {}",
|
||||
adjusted_argument_index,
|
||||
argument_type.display(self.db),
|
||||
expected_ty.display(self.db),
|
||||
);
|
||||
if let Some(specialization) = self.specialization {
|
||||
argument_type = argument_type.apply_specialization(self.db, specialization);
|
||||
expected_ty = expected_ty.apply_specialization(self.db, specialization);
|
||||
eprintln!(
|
||||
"--> after {:?} {} {}",
|
||||
adjusted_argument_index,
|
||||
argument_type.display(self.db),
|
||||
expected_ty.display(self.db),
|
||||
);
|
||||
eprintln!(" {:?}", argument_type);
|
||||
eprintln!(" {:?}", expected_ty);
|
||||
}
|
||||
// This is one of the few places where we want to check if there's _any_ specialization
|
||||
// where assignability holds; normally we want to check that assignability holds for
|
||||
// _all_ specializations.
|
||||
// TODO: Soon we will go further, and build the actual specializations from the
|
||||
// constraint set that we get from this assignability check, instead of inferring and
|
||||
// building them in an earlier separate step.
|
||||
if argument_type
|
||||
let when =
|
||||
argument_type.when_assignable_to(self.db, expected_ty, self.inferable_typevars);
|
||||
eprintln!("===> check argument");
|
||||
eprintln!(" --> arg {}", argument_type.display(self.db));
|
||||
eprintln!(" --> param {}", expected_ty.display(self.db));
|
||||
eprintln!(" --> when {}", when.display(self.db));
|
||||
eprintln!(
|
||||
" --> sat {}",
|
||||
when.satisfied_by_all_typevars(self.db, self.inferable_typevars)
|
||||
);
|
||||
if !argument_type
|
||||
.when_assignable_to(self.db, expected_ty, self.inferable_typevars)
|
||||
.is_never_satisfied(self.db)
|
||||
.satisfied_by_all_typevars(self.db, self.inferable_typevars)
|
||||
{
|
||||
let positional = matches!(argument, Argument::Positional | Argument::Synthetic)
|
||||
&& !parameter.is_variadic();
|
||||
@@ -3116,7 +3194,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||
KnownClass::Str.to_instance(self.db),
|
||||
self.inferable_typevars,
|
||||
)
|
||||
.is_always_satisfied(self.db)
|
||||
.satisfied_by_all_typevars(self.db, self.inferable_typevars)
|
||||
{
|
||||
self.errors.push(BindingError::InvalidKeyType {
|
||||
argument_index: adjusted_argument_index,
|
||||
|
||||
@@ -516,7 +516,7 @@ impl<'db> ClassType<'db> {
|
||||
/// Return `true` if `other` is present in this class's MRO.
|
||||
pub(super) fn is_subclass_of(self, db: &'db dyn Db, other: ClassType<'db>) -> bool {
|
||||
self.when_subclass_of(db, other, InferableTypeVars::None)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
pub(super) fn when_subclass_of(
|
||||
|
||||
@@ -168,15 +168,11 @@ pub struct ConstraintSet<'db> {
|
||||
|
||||
impl<'db> ConstraintSet<'db> {
|
||||
fn never() -> Self {
|
||||
Self {
|
||||
node: Node::AlwaysFalse,
|
||||
}
|
||||
Node::AlwaysFalse.into()
|
||||
}
|
||||
|
||||
fn always() -> Self {
|
||||
Self {
|
||||
node: Node::AlwaysTrue,
|
||||
}
|
||||
Node::AlwaysTrue.into()
|
||||
}
|
||||
|
||||
/// Returns a constraint set that constraints a typevar to a particular range of types.
|
||||
@@ -200,9 +196,7 @@ impl<'db> ConstraintSet<'db> {
|
||||
),
|
||||
};
|
||||
|
||||
Self {
|
||||
node: ConstrainedTypeVar::new_node(db, typevar, lower, upper),
|
||||
}
|
||||
ConstrainedTypeVar::new_node(db, typevar, lower, upper).into()
|
||||
}
|
||||
|
||||
/// Returns whether this constraint set never holds
|
||||
@@ -318,9 +312,7 @@ impl<'db> ConstraintSet<'db> {
|
||||
lhs: Type<'db>,
|
||||
rhs: Type<'db>,
|
||||
) -> Self {
|
||||
Self {
|
||||
node: self.node.implies_subtype_of(db, lhs, rhs),
|
||||
}
|
||||
self.node.implies_subtype_of(db, lhs, rhs).into()
|
||||
}
|
||||
|
||||
/// Returns whether this constraint set is satisfied by all of the typevars that it mentions.
|
||||
@@ -345,6 +337,17 @@ impl<'db> ConstraintSet<'db> {
|
||||
self.node.satisfied_by_all_typevars(db, inferable)
|
||||
}
|
||||
|
||||
/// Returns a new constraint set that ensures that all typevars mentioned in the current
|
||||
/// constraint set have valid specializations, according to any upper bound or constraints they
|
||||
/// might have.
|
||||
pub(crate) fn limit_to_valid_specializations(self, db: &'db dyn Db) -> Self {
|
||||
let mut result = self;
|
||||
self.node.for_each_constraint(db, &mut |constraint| {
|
||||
result.intersect(db, constraint.typevar(db).valid_specializations(db));
|
||||
});
|
||||
result.into()
|
||||
}
|
||||
|
||||
/// Updates this constraint set to hold the union of itself and another constraint set.
|
||||
pub(crate) fn union(&mut self, db: &'db dyn Db, other: Self) -> Self {
|
||||
self.node = self.node.or(db, other.node);
|
||||
@@ -359,9 +362,7 @@ impl<'db> ConstraintSet<'db> {
|
||||
|
||||
/// Returns the negation of this constraint set.
|
||||
pub(crate) fn negate(self, db: &'db dyn Db) -> Self {
|
||||
Self {
|
||||
node: self.node.negate(db),
|
||||
}
|
||||
self.node.negate(db).into()
|
||||
}
|
||||
|
||||
/// Returns the intersection of this constraint set and another. The other constraint set is
|
||||
@@ -390,9 +391,7 @@ impl<'db> ConstraintSet<'db> {
|
||||
}
|
||||
|
||||
pub(crate) fn iff(self, db: &'db dyn Db, other: Self) -> Self {
|
||||
ConstraintSet {
|
||||
node: self.node.iff(db, other.node),
|
||||
}
|
||||
self.node.iff(db, other.node).into()
|
||||
}
|
||||
|
||||
/// Reduces the set of inferable typevars for this constraint set. You provide an iterator of
|
||||
@@ -429,6 +428,12 @@ impl From<bool> for ConstraintSet<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<Node<'db>> for ConstraintSet<'db> {
|
||||
fn from(node: Node<'db>) -> Self {
|
||||
Self { node }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> BoundTypeVarInstance<'db> {
|
||||
/// Returns whether this typevar can be the lower or upper bound of another typevar in a
|
||||
/// constraint set.
|
||||
@@ -1046,7 +1051,7 @@ impl<'db> Node<'db> {
|
||||
// If the typevar is in inferable position, we need to verify that some valid
|
||||
// specialization satisfies the constraint set.
|
||||
let valid_specializations = typevar.valid_specializations(db);
|
||||
if !some_specialization_satisfies(valid_specializations) {
|
||||
if !some_specialization_satisfies(valid_specializations.node) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@@ -2971,7 +2976,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
/// Returns the valid specializations of a typevar. This is used when checking a constraint set
|
||||
/// when this typevar is in inferable position, where we only need _some_ specialization to
|
||||
/// satisfy the constraint set.
|
||||
fn valid_specializations(self, db: &'db dyn Db) -> Node<'db> {
|
||||
pub(crate) fn valid_specializations(self, db: &'db dyn Db) -> ConstraintSet<'db> {
|
||||
// For gradual upper bounds and constraints, we are free to choose any materialization that
|
||||
// makes the check succeed. In inferable positions, it is most helpful to choose a
|
||||
// materialization that is as permissive as possible, since that maximizes the number of
|
||||
@@ -2983,10 +2988,10 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
// that _some_ valid specialization satisfies the constraint set, it's correct for us to
|
||||
// return the range of valid materializations that we can choose from.
|
||||
match self.typevar(db).bound_or_constraints(db) {
|
||||
None => Node::AlwaysTrue,
|
||||
None => ConstraintSet::from(true),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
let bound = bound.top_materialization(db);
|
||||
ConstrainedTypeVar::new_node(db, self, Type::Never, bound)
|
||||
ConstrainedTypeVar::new_node(db, self, Type::Never, bound).into()
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
let mut specializations = Node::AlwaysFalse;
|
||||
@@ -2998,7 +3003,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
ConstrainedTypeVar::new_node(db, self, constraint_lower, constraint_upper),
|
||||
);
|
||||
}
|
||||
specializations
|
||||
specializations.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3055,10 +3060,25 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
}
|
||||
|
||||
impl<'db> GenericContext<'db> {
|
||||
/// Returns the valid specializations of all of the typevars in this generic context.
|
||||
pub(crate) fn valid_specializations(self, db: &'db dyn Db) -> ConstraintSet<'db> {
|
||||
self.variables(db)
|
||||
.when_all(db, |bound_typevar| bound_typevar.valid_specializations(db))
|
||||
}
|
||||
|
||||
pub(crate) fn specialize_constrained(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
constraints: ConstraintSet<'db>,
|
||||
) -> Result<Specialization<'db>, ()> {
|
||||
self.specialize_constrained_mapped(db, constraints, |_, _, ty| ty)
|
||||
}
|
||||
|
||||
pub(crate) fn specialize_constrained_mapped(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
constraints: ConstraintSet<'db>,
|
||||
f: impl Fn(BoundTypeVarIdentity<'db>, BoundTypeVarInstance<'db>, Type<'db>) -> Type<'db>,
|
||||
) -> Result<Specialization<'db>, ()> {
|
||||
// If the constraint set is cyclic, don't even try to construct a specialization.
|
||||
if constraints.is_cyclic(db) {
|
||||
@@ -3072,7 +3092,7 @@ impl<'db> GenericContext<'db> {
|
||||
let abstracted = self
|
||||
.variables(db)
|
||||
.fold(constraints.node, |constraints, bound_typevar| {
|
||||
constraints.and(db, bound_typevar.valid_specializations(db))
|
||||
constraints.and(db, bound_typevar.valid_specializations(db).node)
|
||||
});
|
||||
|
||||
// Then we find all of the "representative types" for each typevar in the constraint set.
|
||||
@@ -3091,17 +3111,14 @@ impl<'db> GenericContext<'db> {
|
||||
let mut satisfied = false;
|
||||
let mut greatest_lower_bound = Type::Never;
|
||||
let mut least_upper_bound = Type::object();
|
||||
abstracted.find_representative_types(
|
||||
db,
|
||||
bound_typevar.identity(db),
|
||||
|lower_bound, upper_bound| {
|
||||
satisfied = true;
|
||||
greatest_lower_bound =
|
||||
UnionType::from_elements(db, [greatest_lower_bound, lower_bound]);
|
||||
least_upper_bound =
|
||||
IntersectionType::from_elements(db, [least_upper_bound, upper_bound]);
|
||||
},
|
||||
);
|
||||
let identity = bound_typevar.identity(db);
|
||||
abstracted.find_representative_types(db, identity, |lower_bound, upper_bound| {
|
||||
satisfied = true;
|
||||
greatest_lower_bound =
|
||||
UnionType::from_elements(db, [greatest_lower_bound, lower_bound]);
|
||||
least_upper_bound =
|
||||
IntersectionType::from_elements(db, [least_upper_bound, upper_bound]);
|
||||
});
|
||||
|
||||
// If there are no satisfiable paths in the BDD, then there is no valid specialization
|
||||
// for this constraint set.
|
||||
@@ -3119,7 +3136,7 @@ impl<'db> GenericContext<'db> {
|
||||
|
||||
// Of all of the types that satisfy all of the paths in the BDD, we choose the
|
||||
// "largest" one (i.e., "closest to `object`") as the specialization.
|
||||
types[i] = least_upper_bound;
|
||||
types[i] = f(identity, bound_typevar, least_upper_bound);
|
||||
}
|
||||
|
||||
Ok(self.specialize_recursive(db, types.into_boxed_slice()))
|
||||
|
||||
@@ -1432,6 +1432,7 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
mut f: &mut dyn FnMut(TypeVarAssignment<'db>) -> Option<Type<'db>>,
|
||||
) -> Result<(), SpecializationError<'db>> {
|
||||
if formal == actual {
|
||||
eprintln!(" --> AAA");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1506,10 +1507,11 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
if !actual.is_never() {
|
||||
let assignable_elements = (formal.elements(self.db).iter()).filter(|ty| {
|
||||
actual
|
||||
.when_subtype_of(self.db, **ty, self.inferable)
|
||||
.is_always_satisfied(self.db)
|
||||
.when_subtype_of(self.db, **ty, InferableTypeVars::None)
|
||||
.satisfied_by_all_typevars(self.db, InferableTypeVars::None)
|
||||
});
|
||||
if assignable_elements.exactly_one().is_ok() {
|
||||
eprintln!(" --> BBB");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -1538,7 +1540,7 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
if !ty
|
||||
.when_assignable_to(self.db, bound, self.inferable)
|
||||
.is_always_satisfied(self.db)
|
||||
.satisfied_by_all_typevars(self.db, self.inferable)
|
||||
{
|
||||
return Err(SpecializationError::MismatchedBound {
|
||||
bound_typevar,
|
||||
@@ -1559,7 +1561,7 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
for constraint in constraints.elements(self.db) {
|
||||
if ty
|
||||
.when_assignable_to(self.db, *constraint, self.inferable)
|
||||
.is_always_satisfied(self.db)
|
||||
.satisfied_by_all_typevars(self.db, self.inferable)
|
||||
{
|
||||
self.add_type_mapping(bound_typevar, *constraint, polarity, f);
|
||||
return Ok(());
|
||||
|
||||
@@ -679,7 +679,7 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
)
|
||||
.is_always_satisfied(db)
|
||||
.satisfied_by_all_typevars(db, InferableTypeVars::None)
|
||||
}
|
||||
|
||||
fn initial<'db>(
|
||||
|
||||
@@ -876,19 +876,22 @@ impl<'db> Signature<'db> {
|
||||
let mut check_types = |type1: Option<Type<'db>>, type2: Option<Type<'db>>| {
|
||||
let type1 = type1.unwrap_or(Type::unknown());
|
||||
let type2 = type2.unwrap_or(Type::unknown());
|
||||
!result
|
||||
.intersect(
|
||||
db,
|
||||
type1.has_relation_to_impl(
|
||||
db,
|
||||
type2,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
),
|
||||
)
|
||||
.is_never_satisfied(db)
|
||||
eprintln!(" --> check param");
|
||||
eprintln!(" {}", type1.display(db));
|
||||
eprintln!(" {}", type2.display(db));
|
||||
let x = type1.has_relation_to_impl(
|
||||
db,
|
||||
type2,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
);
|
||||
eprintln!(" x {}", x.display(db),);
|
||||
let y = result.intersect(db, x);
|
||||
eprintln!(" y {}", y.display(db),);
|
||||
eprintln!(" ? {}", !y.is_never_satisfied(db));
|
||||
!y.is_never_satisfied(db)
|
||||
};
|
||||
|
||||
// Return types are covariant.
|
||||
@@ -932,6 +935,7 @@ impl<'db> Signature<'db> {
|
||||
let Some(next_parameter) = parameters.next() else {
|
||||
// All parameters have been checked or both the parameter lists were empty. In
|
||||
// either case, `self` is a subtype of `other`.
|
||||
eprintln!(" --> X1 {}", result.display(db));
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -952,6 +956,7 @@ impl<'db> Signature<'db> {
|
||||
// `other`, then the non-variadic parameters in `self` must have a default
|
||||
// value.
|
||||
if default_type.is_none() {
|
||||
eprintln!(" --> X2");
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
}
|
||||
@@ -964,6 +969,7 @@ impl<'db> Signature<'db> {
|
||||
EitherOrBoth::Right(_) => {
|
||||
// If there are more parameters in `other` than in `self`, then `self` is not a
|
||||
// subtype of `other`.
|
||||
eprintln!(" --> X3");
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
|
||||
@@ -984,12 +990,14 @@ impl<'db> Signature<'db> {
|
||||
},
|
||||
) => {
|
||||
if self_default.is_none() && other_default.is_some() {
|
||||
eprintln!(" --> X4");
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
if !check_types(
|
||||
other_parameter.annotated_type(),
|
||||
self_parameter.annotated_type(),
|
||||
) {
|
||||
eprintln!(" --> X5");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1005,16 +1013,19 @@ impl<'db> Signature<'db> {
|
||||
},
|
||||
) => {
|
||||
if self_name != other_name {
|
||||
eprintln!(" --> X6");
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
// The following checks are the same as positional-only parameters.
|
||||
if self_default.is_none() && other_default.is_some() {
|
||||
eprintln!(" --> X7");
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
if !check_types(
|
||||
other_parameter.annotated_type(),
|
||||
self_parameter.annotated_type(),
|
||||
) {
|
||||
eprintln!(" --> X8");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1028,6 +1039,7 @@ impl<'db> Signature<'db> {
|
||||
other_parameter.annotated_type(),
|
||||
self_parameter.annotated_type(),
|
||||
) {
|
||||
eprintln!(" --> X9");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1068,6 +1080,7 @@ impl<'db> Signature<'db> {
|
||||
other_parameter.annotated_type(),
|
||||
self_parameter.annotated_type(),
|
||||
) {
|
||||
eprintln!(" --> X10");
|
||||
return result;
|
||||
}
|
||||
parameters.next_other();
|
||||
@@ -1079,6 +1092,7 @@ impl<'db> Signature<'db> {
|
||||
other_parameter.annotated_type(),
|
||||
self_parameter.annotated_type(),
|
||||
) {
|
||||
eprintln!(" --> X11");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1094,12 +1108,16 @@ impl<'db> Signature<'db> {
|
||||
break;
|
||||
}
|
||||
|
||||
_ => return ConstraintSet::from(false),
|
||||
_ => {
|
||||
eprintln!(" --> X12");
|
||||
return ConstraintSet::from(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!(" --> YYY");
|
||||
// At this point, the remaining parameters in `other` are keyword-only or keyword variadic.
|
||||
// But, `self` could contain any unmatched positional parameters.
|
||||
let (self_parameters, other_parameters) = parameters.into_remaining();
|
||||
|
||||
Reference in New Issue
Block a user