add with_inferable mdtest function

This commit is contained in:
Douglas Creager 2025-11-08 10:04:01 -05:00
parent b033a42ced
commit e6ec4062d5
5 changed files with 143 additions and 102 deletions

View File

@ -60,29 +60,29 @@ class Sub(Base): ...
class Unrelated: ... class Unrelated: ...
def unbounded[T](): def unbounded[T]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated). # (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated). # (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which satisfies (T ≤ Super). # (T = Base) is a valid specialization, which satisfies (T ≤ Super).
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super). # (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which satisfies (T ≤ Base). # (T = Base) is a valid specialization, which satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base). # (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub). # (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
static_assert(ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Sub).with_inferable(T).satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Sub). # (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Sub).
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
``` ```
@ -106,38 +106,38 @@ class Sub(Base): ...
class Unrelated: ... class Unrelated: ...
def bounded[T: Base](): def bounded[T: Base]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which satisfies (T ≤ Super). # (T = Base) is a valid specialization, which satisfies (T ≤ Super).
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
# Every valid specialization satisfies (T ≤ Base). Since (Base ≤ Super), every valid # Every valid specialization satisfies (T ≤ Base). Since (Base ≤ Super), every valid
# specialization also satisfies (T ≤ Super). # specialization also satisfies (T ≤ Super).
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which satisfies (T ≤ Base). # (T = Base) is a valid specialization, which satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
# Every valid specialization satisfies (T ≤ Base). # Every valid specialization satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub). # (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
static_assert(ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Sub).with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub). # (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub).
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated). # (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
constraints = ConstraintSet.range(Never, T, Unrelated) constraints = ConstraintSet.range(Never, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated). # (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
# Never is the only type that satisfies both (T ≤ Base) and (T ≤ Unrelated). So there is no # Never is the only type that satisfies both (T ≤ Base) and (T ≤ Unrelated). So there is no
# valid specialization that satisfies (T ≤ Unrelated ∧ T ≠ Never). # valid specialization that satisfies (T ≤ Unrelated ∧ T ≠ Never).
constraints = constraints & ~ConstraintSet.range(Never, T, Never) constraints = constraints & ~ConstraintSet.range(Never, T, Never)
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not constraints.with_inferable(T).satisfied_by_all_typevars())
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
``` ```
@ -153,15 +153,15 @@ the constraint set.
from typing import Any from typing import Any
def bounded_by_gradual[T: Any](): def bounded_by_gradual[T: Any]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# If we choose Base as the materialization for the upper bound, then (T = Base) is a valid # If we choose Base as the materialization for the upper bound, then (T = Base) is a valid
# specialization, which satisfies (T ≤ Base). # specialization, which satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
# We are free to choose any materialization of the upper bound, and only have to show that the # We are free to choose any materialization of the upper bound, and only have to show that the
# constraint set holds for that one materialization. Having chosen one materialization, we then # constraint set holds for that one materialization. Having chosen one materialization, we then
# have to show that the constraint set holds for all valid specializations of that # have to show that the constraint set holds for all valid specializations of that
@ -173,7 +173,7 @@ def bounded_by_gradual[T: Any]():
# If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization, # If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization,
# which satisfies (T ≤ Unrelated). # which satisfies (T ≤ Unrelated).
constraints = ConstraintSet.range(Never, T, Unrelated) constraints = ConstraintSet.range(Never, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# If we choose Never as the materialization, then (T = Never) is the only valid specialization, # If we choose Never as the materialization, then (T = Never) is the only valid specialization,
# which satisfies (T ≤ Unrelated). # which satisfies (T ≤ Unrelated).
static_assert(constraints.satisfied_by_all_typevars()) static_assert(constraints.satisfied_by_all_typevars())
@ -181,7 +181,7 @@ def bounded_by_gradual[T: Any]():
# If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization, # If we choose Unrelated as the materialization, then (T = Unrelated) is a valid specialization,
# which satisfies (T ≤ Unrelated ∧ T ≠ Never). # which satisfies (T ≤ Unrelated ∧ T ≠ Never).
constraints = constraints & ~ConstraintSet.range(Never, T, Never) constraints = constraints & ~ConstraintSet.range(Never, T, Never)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# There is no upper bound that we can choose to satisfy this constraint set in non-inferable # There is no upper bound that we can choose to satisfy this constraint set in non-inferable
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
# (T ≤ Unrelated ∧ T ≠ Never). # (T ≤ Unrelated ∧ T ≠ Never).
@ -196,15 +196,15 @@ restrictive variance (i.e., invariance), but we get the same results for other v
```py ```py
def bounded_by_gradual[T: list[Any]](): def bounded_by_gradual[T: list[Any]]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# If we choose list[Base] as the materialization of the upper bound, then (T = list[Base]) is a # If we choose list[Base] as the materialization of the upper bound, then (T = list[Base]) is a
# valid specialization, which satisfies (T ≤ list[Base]). # valid specialization, which satisfies (T ≤ list[Base]).
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Base]).with_inferable(T).satisfied_by_all_typevars())
# If we choose Base as the materialization, then all valid specializations must satisfy # If we choose Base as the materialization, then all valid specializations must satisfy
# (T ≤ list[Base]). # (T ≤ list[Base]).
# We are free to choose any materialization of the upper bound, and only have to show that the # We are free to choose any materialization of the upper bound, and only have to show that the
@ -217,7 +217,7 @@ def bounded_by_gradual[T: list[Any]]():
# If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid # If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid
# specialization, which satisfies (T ≤ list[Unrelated]). # specialization, which satisfies (T ≤ list[Unrelated]).
constraints = ConstraintSet.range(Never, T, list[Unrelated]) constraints = ConstraintSet.range(Never, T, list[Unrelated])
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# If we choose Unrelated as the materialization, then all valid specializations must satisfy # If we choose Unrelated as the materialization, then all valid specializations must satisfy
# (T ≤ list[Unrelated]). # (T ≤ list[Unrelated]).
static_assert(constraints.satisfied_by_all_typevars()) static_assert(constraints.satisfied_by_all_typevars())
@ -225,7 +225,7 @@ def bounded_by_gradual[T: list[Any]]():
# If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid # If we choose Unrelated as the materialization, then (T = list[Unrelated]) is a valid
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never). # specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
constraints = constraints & ~ConstraintSet.range(Never, T, Never) constraints = constraints & ~ConstraintSet.range(Never, T, Never)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# There is no upper bound that we can choose to satisfy this constraint set in non-inferable # There is no upper bound that we can choose to satisfy this constraint set in non-inferable
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
# (T ≤ list[Unrelated] ∧ T ≠ Never). # (T ≤ list[Unrelated] ∧ T ≠ Never).
@ -251,61 +251,61 @@ class Sub(Base): ...
class Unrelated: ... class Unrelated: ...
def constrained[T: (Base, Unrelated)](): def constrained[T: (Base, Unrelated)]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Unrelated). # (T = Unrelated) is a valid specialization, which satisfies (T ≤ Unrelated).
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated). # (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which satisfies (T ≤ Super). # (T = Base) is a valid specialization, which satisfies (T ≤ Super).
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super). # (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which satisfies (T ≤ Base). # (T = Base) is a valid specialization, which satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base). # (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
# Neither (T = Base) nor (T = Unrelated) satisfy (T ≤ Sub). # Neither (T = Base) nor (T = Unrelated) satisfy (T ≤ Sub).
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.range(Never, T, Sub).with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Super T ≤ Unrelated). # (T = Base) and (T = Unrelated) both satisfy (T ≤ Super T ≤ Unrelated).
constraints = ConstraintSet.range(Never, T, Super) | ConstraintSet.range(Never, T, Unrelated) constraints = ConstraintSet.range(Never, T, Super) | ConstraintSet.range(Never, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
static_assert(constraints.satisfied_by_all_typevars()) static_assert(constraints.satisfied_by_all_typevars())
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Base T ≤ Unrelated). # (T = Base) and (T = Unrelated) both satisfy (T ≤ Base T ≤ Unrelated).
constraints = ConstraintSet.range(Never, T, Base) | ConstraintSet.range(Never, T, Unrelated) constraints = ConstraintSet.range(Never, T, Base) | ConstraintSet.range(Never, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
static_assert(constraints.satisfied_by_all_typevars()) static_assert(constraints.satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Sub T ≤ Unrelated). # (T = Unrelated) is a valid specialization, which satisfies (T ≤ Sub T ≤ Unrelated).
constraints = ConstraintSet.range(Never, T, Sub) | ConstraintSet.range(Never, T, Unrelated) constraints = ConstraintSet.range(Never, T, Sub) | ConstraintSet.range(Never, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub T ≤ Unrelated). # (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub T ≤ Unrelated).
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which satisfies (T = Super T = Unrelated). # (T = Unrelated) is a valid specialization, which satisfies (T = Super T = Unrelated).
constraints = ConstraintSet.range(Super, T, Super) | ConstraintSet.range(Unrelated, T, Unrelated) constraints = ConstraintSet.range(Super, T, Super) | ConstraintSet.range(Unrelated, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T = Super T = Unrelated). # (T = Base) is a valid specialization, which does not satisfy (T = Super T = Unrelated).
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
# (T = Base) and (T = Unrelated) both satisfy (T = Base T = Unrelated). # (T = Base) and (T = Unrelated) both satisfy (T = Base T = Unrelated).
constraints = ConstraintSet.range(Base, T, Base) | ConstraintSet.range(Unrelated, T, Unrelated) constraints = ConstraintSet.range(Base, T, Base) | ConstraintSet.range(Unrelated, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
static_assert(constraints.satisfied_by_all_typevars()) static_assert(constraints.satisfied_by_all_typevars())
# (T = Unrelated) is a valid specialization, which satisfies (T = Sub T = Unrelated). # (T = Unrelated) is a valid specialization, which satisfies (T = Sub T = Unrelated).
constraints = ConstraintSet.range(Sub, T, Sub) | ConstraintSet.range(Unrelated, T, Unrelated) constraints = ConstraintSet.range(Sub, T, Sub) | ConstraintSet.range(Unrelated, T, Unrelated)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# (T = Base) is a valid specialization, which does not satisfy (T = Sub T = Unrelated). # (T = Base) is a valid specialization, which does not satisfy (T = Sub T = Unrelated).
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
``` ```
@ -322,50 +322,50 @@ satisfy the constraint set.
from typing import Any from typing import Any
def constrained_by_gradual[T: (Base, Any)](): def constrained_by_gradual[T: (Base, Any)]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# If we choose Unrelated as the materialization of the gradual constraint, then (T = Unrelated) # If we choose Unrelated as the materialization of the gradual constraint, then (T = Unrelated)
# is a valid specialization, which satisfies (T ≤ Unrelated). # is a valid specialization, which satisfies (T ≤ Unrelated).
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = Base) is a valid specialization, which does # No matter which materialization we choose, (T = Base) is a valid specialization, which does
# not satisfy (T ≤ Unrelated). # not satisfy (T ≤ Unrelated).
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
# If we choose Super as the materialization, then (T = Super) is a valid specialization, which # If we choose Super as the materialization, then (T = Super) is a valid specialization, which
# satisfies (T ≤ Super). # satisfies (T ≤ Super).
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Super).with_inferable(T).satisfied_by_all_typevars())
# If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid # If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid
# specializations, both of which satisfy (T ≤ Super). # specializations, both of which satisfy (T ≤ Super).
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
# If we choose Base as the materialization, then (T = Base) is a valid specialization, which # If we choose Base as the materialization, then (T = Base) is a valid specialization, which
# satisfies (T ≤ Base). # satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
# If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid # If we choose Never as the materialization, then (T = Base) and (T = Never) are the only valid
# specializations, both of which satisfy (T ≤ Base). # specializations, both of which satisfy (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
def constrained_by_two_gradual[T: (Any, Any)](): def constrained_by_two_gradual[T: (Any, Any)]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# If we choose Unrelated as the materialization of either constraint, then (T = Unrelated) is a # If we choose Unrelated as the materialization of either constraint, then (T = Unrelated) is a
# valid specialization, which satisfies (T ≤ Unrelated). # valid specialization, which satisfies (T ≤ Unrelated).
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
# If we choose Unrelated as the materialization of both constraints, then (T = Unrelated) is the # If we choose Unrelated as the materialization of both constraints, then (T = Unrelated) is the
# only valid specialization, which satisfies (T ≤ Unrelated). # only valid specialization, which satisfies (T ≤ Unrelated).
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
# If we choose Base as the materialization of either constraint, then (T = Base) is a valid # If we choose Base as the materialization of either constraint, then (T = Base) is a valid
# specialization, which satisfies (T ≤ Base). # specialization, which satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, Base).with_inferable(T).satisfied_by_all_typevars())
# If we choose Never as the materialization of both constraints, then (T = Never) is the only # If we choose Never as the materialization of both constraints, then (T = Never) is the only
# valid specialization, which satisfies (T ≤ Base). # valid specialization, which satisfies (T ≤ Base).
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
@ -379,35 +379,35 @@ restrictive variance (i.e., invariance), but we get the same results for other v
```py ```py
def constrained_by_gradual[T: (list[Base], list[Any])](): def constrained_by_gradual[T: (list[Base], list[Any])]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# No matter which materialization we choose, every valid specialization will be of the form # No matter which materialization we choose, every valid specialization will be of the form
# (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore # (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore
# no materialization or specialization that satisfies (T ≤ Unrelated). # no materialization or specialization that satisfies (T ≤ Unrelated).
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
# If we choose list[Super] as the materialization, then (T = list[Super]) is a valid # If we choose list[Super] as the materialization, then (T = list[Super]) is a valid
# specialization, which satisfies (T ≤ list[Super]). # specialization, which satisfies (T ≤ list[Super]).
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Super]).with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which # No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
# does not satisfy (T ≤ list[Super]). # does not satisfy (T ≤ list[Super]).
static_assert(not ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars())
# If we choose list[Base] as the materialization, then (T = list[Base]) is a valid # If we choose list[Base] as the materialization, then (T = list[Base]) is a valid
# specialization, which satisfies (T ≤ list[Base]). # specialization, which satisfies (T ≤ list[Base]).
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Base]).with_inferable(T).satisfied_by_all_typevars())
# If we choose list[Base] as the materialization, then all valid specializations must satisfy # If we choose list[Base] as the materialization, then all valid specializations must satisfy
# (T ≤ list[Base]). # (T ≤ list[Base]).
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars())
# If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization, # If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization,
# which # satisfies (T ≤ list[Sub]). # which # satisfies (T ≤ list[Sub]).
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Sub]).with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which # No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
# does not satisfy (T ≤ list[Sub]). # does not satisfy (T ≤ list[Sub]).
static_assert(not ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars())
@ -415,7 +415,7 @@ def constrained_by_gradual[T: (list[Base], list[Any])]():
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid # If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
# specialization, which satisfies (T ≤ list[Unrelated]). # specialization, which satisfies (T ≤ list[Unrelated]).
constraints = ConstraintSet.range(Never, T, list[Unrelated]) constraints = ConstraintSet.range(Never, T, list[Unrelated])
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which # No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
# does not satisfy (T ≤ list[Unrelated]). # does not satisfy (T ≤ list[Unrelated]).
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
@ -423,42 +423,42 @@ def constrained_by_gradual[T: (list[Base], list[Any])]():
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid # If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never). # specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
constraints = constraints & ~ConstraintSet.range(Never, T, Never) constraints = constraints & ~ConstraintSet.range(Never, T, Never)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# There is no materialization that we can choose to satisfy this constraint set in non-inferable # There is no materialization that we can choose to satisfy this constraint set in non-inferable
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
# (T ≤ list[Unrelated] ∧ T ≠ Never). # (T ≤ list[Unrelated] ∧ T ≠ Never).
static_assert(not constraints.satisfied_by_all_typevars()) static_assert(not constraints.satisfied_by_all_typevars())
def constrained_by_two_gradual[T: (list[Any], list[Any])](): def constrained_by_two_gradual[T: (list[Any], list[Any])]():
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.always().with_inferable(T).satisfied_by_all_typevars())
static_assert(ConstraintSet.always().satisfied_by_all_typevars()) static_assert(ConstraintSet.always().satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.never().with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.never().satisfied_by_all_typevars()) static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
# No matter which materialization we choose, every valid specialization will be of the form # No matter which materialization we choose, every valid specialization will be of the form
# (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore # (T = list[X]). Because Unrelated is final, it is disjoint from all lists. There is therefore
# no materialization or specialization that satisfies (T ≤ Unrelated). # no materialization or specialization that satisfies (T ≤ Unrelated).
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(not ConstraintSet.range(Never, T, Unrelated).with_inferable(T).satisfied_by_all_typevars())
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
# If we choose list[Super] as the materialization, then (T = list[Super]) is a valid # If we choose list[Super] as the materialization, then (T = list[Super]) is a valid
# specialization, which satisfies (T ≤ list[Super]). # specialization, which satisfies (T ≤ list[Super]).
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Super]).with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which # No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
# does not satisfy (T ≤ list[Super]). # does not satisfy (T ≤ list[Super]).
static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, list[Super]).satisfied_by_all_typevars())
# If we choose list[Base] as the materialization, then (T = list[Base]) is a valid # If we choose list[Base] as the materialization, then (T = list[Base]) is a valid
# specialization, which satisfies (T ≤ list[Base]). # specialization, which satisfies (T ≤ list[Base]).
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Base]).with_inferable(T).satisfied_by_all_typevars())
# If we choose Base as the materialization, then all valid specializations must satisfy # If we choose Base as the materialization, then all valid specializations must satisfy
# (T ≤ list[Base]). # (T ≤ list[Base]).
static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, list[Base]).satisfied_by_all_typevars())
# If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization, # If we choose list[Sub] as the materialization, then (T = list[Sub]) is a valid specialization,
# which satisfies (T ≤ list[Sub]). # which satisfies (T ≤ list[Sub]).
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars(inferable=tuple[T])) static_assert(ConstraintSet.range(Never, T, list[Sub]).with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which # No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
# does not satisfy (T ≤ list[Sub]). # does not satisfy (T ≤ list[Sub]).
static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars()) static_assert(ConstraintSet.range(Never, T, list[Sub]).satisfied_by_all_typevars())
@ -466,7 +466,7 @@ def constrained_by_two_gradual[T: (list[Any], list[Any])]():
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid # If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
# specialization, which satisfies (T ≤ list[Unrelated]). # specialization, which satisfies (T ≤ list[Unrelated]).
constraints = ConstraintSet.range(Never, T, list[Unrelated]) constraints = ConstraintSet.range(Never, T, list[Unrelated])
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# No matter which materialization we choose, (T = list[Base]) is a valid specialization, which # No matter which materialization we choose, (T = list[Base]) is a valid specialization, which
# does not satisfy (T ≤ list[Unrelated]). # does not satisfy (T ≤ list[Unrelated]).
static_assert(constraints.satisfied_by_all_typevars()) static_assert(constraints.satisfied_by_all_typevars())
@ -474,7 +474,7 @@ def constrained_by_two_gradual[T: (list[Any], list[Any])]():
# If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid # If we choose list[Unrelated] as the materialization, then (T = list[Unrelated]) is a valid
# specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never). # specialization, which satisfies (T ≤ list[Unrelated] ∧ T ≠ Never).
constraints = constraints & ~ConstraintSet.range(Never, T, Never) constraints = constraints & ~ConstraintSet.range(Never, T, Never)
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T])) static_assert(constraints.with_inferable(T).satisfied_by_all_typevars())
# There is no constraint that we can choose to satisfy this constraint set in non-inferable # There is no constraint that we can choose to satisfy this constraint set in non-inferable
# position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy
# (T ≤ list[Unrelated] ∧ T ≠ Never). # (T ≤ list[Unrelated] ∧ T ≠ Never).

View File

@ -4317,6 +4317,14 @@ impl<'db> Type<'db> {
)) ))
.into() .into()
} }
Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked))
if name == "with_inferable" =>
{
Place::bound(Type::KnownBoundMethod(
KnownBoundMethodType::ConstraintSetWithInferable(tracked),
))
.into()
}
Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked)) Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked))
if name == "implies_subtype_of" => if name == "implies_subtype_of" =>
{ {
@ -7162,6 +7170,7 @@ impl<'db> Type<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_)
@ -7322,6 +7331,7 @@ impl<'db> Type<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_),
@ -10780,6 +10790,7 @@ pub enum KnownBoundMethodType<'db> {
ConstraintSetRange, ConstraintSetRange,
ConstraintSetAlways, ConstraintSetAlways,
ConstraintSetNever, ConstraintSetNever,
ConstraintSetWithInferable(TrackedConstraintSet<'db>),
ConstraintSetImpliesSubtypeOf(TrackedConstraintSet<'db>), ConstraintSetImpliesSubtypeOf(TrackedConstraintSet<'db>),
ConstraintSetSatisfies(TrackedConstraintSet<'db>), ConstraintSetSatisfies(TrackedConstraintSet<'db>),
ConstraintSetSatisfiedByAllTypeVars(TrackedConstraintSet<'db>), ConstraintSetSatisfiedByAllTypeVars(TrackedConstraintSet<'db>),
@ -10810,6 +10821,7 @@ pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Size
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => {} | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => {}
@ -10877,6 +10889,10 @@ impl<'db> KnownBoundMethodType<'db> {
KnownBoundMethodType::ConstraintSetNever, KnownBoundMethodType::ConstraintSetNever,
KnownBoundMethodType::ConstraintSetNever, KnownBoundMethodType::ConstraintSetNever,
) )
| (
KnownBoundMethodType::ConstraintSetWithInferable(_),
KnownBoundMethodType::ConstraintSetWithInferable(_),
)
| ( | (
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_),
@ -10900,6 +10916,7 @@ impl<'db> KnownBoundMethodType<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_),
@ -10912,6 +10929,7 @@ impl<'db> KnownBoundMethodType<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_),
@ -10965,6 +10983,10 @@ impl<'db> KnownBoundMethodType<'db> {
) => ConstraintSet::from(true), ) => ConstraintSet::from(true),
( (
KnownBoundMethodType::ConstraintSetWithInferable(left_constraints),
KnownBoundMethodType::ConstraintSetWithInferable(right_constraints),
)
| (
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(left_constraints), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(left_constraints),
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(right_constraints), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(right_constraints),
) )
@ -10989,6 +11011,7 @@ impl<'db> KnownBoundMethodType<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_),
@ -11001,6 +11024,7 @@ impl<'db> KnownBoundMethodType<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_),
@ -11027,6 +11051,7 @@ impl<'db> KnownBoundMethodType<'db> {
| KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => self, | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => self,
@ -11045,6 +11070,7 @@ impl<'db> KnownBoundMethodType<'db> {
KnownBoundMethodType::ConstraintSetRange KnownBoundMethodType::ConstraintSetRange
| KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetAlways
| KnownBoundMethodType::ConstraintSetNever | KnownBoundMethodType::ConstraintSetNever
| KnownBoundMethodType::ConstraintSetWithInferable(_)
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
| KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfies(_)
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => { | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => {
@ -11175,6 +11201,15 @@ impl<'db> KnownBoundMethodType<'db> {
))) )))
} }
KnownBoundMethodType::ConstraintSetWithInferable(_) => {
Either::Right(std::iter::once(Signature::new(
Parameters::new([Parameter::variadic(Name::new_static("inferable"))
.type_form()
.with_annotated_type(Type::any())]),
Some(KnownClass::ConstraintSet.to_instance(db)),
)))
}
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => { KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => {
Either::Right(std::iter::once(Signature::new( Either::Right(std::iter::once(Signature::new(
Parameters::new([ Parameters::new([
@ -11199,13 +11234,7 @@ impl<'db> KnownBoundMethodType<'db> {
KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => { KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => {
Either::Right(std::iter::once(Signature::new( Either::Right(std::iter::once(Signature::new(
Parameters::new([Parameter::keyword_only(Name::new_static("inferable")) Parameters::empty(),
.type_form()
.with_annotated_type(UnionType::from_elements(
db,
[Type::homogeneous_tuple(db, Type::any()), Type::none(db)],
))
.with_default_type(Type::none(db))]),
Some(KnownClass::Bool.to_instance(db)), Some(KnownClass::Bool.to_instance(db)),
))) )))
} }

View File

@ -36,9 +36,9 @@ use crate::types::tuple::{TupleLength, TupleType};
use crate::types::{ use crate::types::{
BoundMethodType, BoundTypeVarIdentity, ClassLiteral, DataclassFlags, DataclassParams, BoundMethodType, BoundTypeVarIdentity, ClassLiteral, DataclassFlags, DataclassParams,
FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy,
NominalInstanceType, PropertyInstanceType, SpecialFormType, TrackedConstraintSet, PropertyInstanceType, SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext,
TypeAliasType, TypeContext, UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, infer_isolated_expression,
infer_isolated_expression, todo_type, todo_type,
}; };
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity}; use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion}; use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion};
@ -181,7 +181,7 @@ impl<'db> Bindings<'db> {
} }
} }
self.evaluate_known_cases(db, dataclass_field_specifiers); self.evaluate_known_cases(db, argument_types, dataclass_field_specifiers);
// In order of precedence: // In order of precedence:
// //
@ -300,7 +300,12 @@ impl<'db> Bindings<'db> {
/// Evaluates the return type of certain known callables, where we have special-case logic to /// Evaluates the return type of certain known callables, where we have special-case logic to
/// determine the return type in a way that isn't directly expressible in the type system. /// determine the return type in a way that isn't directly expressible in the type system.
fn evaluate_known_cases(&mut self, db: &'db dyn Db, dataclass_field_specifiers: &[Type<'db>]) { fn evaluate_known_cases(
&mut self,
db: &'db dyn Db,
argument_types: &CallArguments<'_, 'db>,
dataclass_field_specifiers: &[Type<'db>],
) {
let to_bool = |ty: &Option<Type<'_>>, default: bool| -> bool { let to_bool = |ty: &Option<Type<'_>>, default: bool| -> bool {
if let Some(Type::BooleanLiteral(value)) = ty { if let Some(Type::BooleanLiteral(value)) = ty {
*value *value
@ -1177,6 +1182,33 @@ impl<'db> Bindings<'db> {
)); ));
} }
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetWithInferable(
tracked,
)) => {
let mut any_invalid = false;
let inferable = InferableTypeVars::from_bound_typevars(
db,
overload
.arguments_for_parameter(argument_types, 0)
.filter_map(|(_, ty)| {
let identity = ty
.as_typevar()
.map(|bound_typevar| bound_typevar.identity(db));
any_invalid |= identity.is_none();
identity
}),
);
if any_invalid {
continue;
}
let result = tracked.constraints(db).with_inferable(inferable);
let tracked = TrackedConstraintSet::new(db, result);
overload.set_return_type(Type::KnownInstance(
KnownInstanceType::ConstraintSet(tracked),
));
}
Type::KnownBoundMethod( Type::KnownBoundMethod(
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked),
) => { ) => {
@ -1214,36 +1246,7 @@ impl<'db> Bindings<'db> {
Type::KnownBoundMethod( Type::KnownBoundMethod(
KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(tracked), KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(tracked),
) => { ) => {
let extract_inferable = |instance: &NominalInstanceType<'db>| { let result = tracked.constraints(db).satisfied_by_all_typevars(db);
if instance.has_known_class(db, KnownClass::NoneType) {
// Caller explicitly passed None, so no typevars are inferable.
return Some(InferableTypeVars::none());
}
Some(InferableTypeVars::from_bound_typevars(
db,
instance.tuple_spec(db)?.fixed_elements().filter_map(|ty| {
ty.as_typevar()
.map(|bound_typevar| bound_typevar.identity(db))
}),
))
};
let inferable = match overload.parameter_types() {
// Caller did not provide argument, so no typevars are inferable.
[None] => InferableTypeVars::none(),
[Some(Type::NominalInstance(instance))] => {
match extract_inferable(instance) {
Some(inferable) => inferable,
None => continue,
}
}
_ => continue,
};
let result = tracked
.constraints(db)
.with_inferable(inferable)
.satisfied_by_all_typevars(db);
overload.set_return_type(Type::BooleanLiteral(result)); overload.set_return_type(Type::BooleanLiteral(result));
} }

View File

@ -532,6 +532,9 @@ impl Display for DisplayRepresentation<'_> {
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => { Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => {
f.write_str("bound method `ConstraintSet.never`") f.write_str("bound method `ConstraintSet.never`")
} }
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetWithInferable(_)) => {
f.write_str("bound method `ConstraintSet.with_inferable`")
}
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)) => { Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)) => {
f.write_str("bound method `ConstraintSet.implies_subtype_of`") f.write_str("bound method `ConstraintSet.implies_subtype_of`")
} }

View File

@ -59,6 +59,12 @@ class ConstraintSet:
def never() -> Self: def never() -> Self:
"""Returns a constraint set that is never satisfied""" """Returns a constraint set that is never satisfied"""
def with_inferable(self, *inferable: Any) -> Self:
"""
Returns a copy of this constraint set with some typevars marked as being
in inferable position.
"""
def implies_subtype_of(self, ty: Any, of: Any) -> Self: def implies_subtype_of(self, ty: Any, of: Any) -> Self:
""" """
Returns a constraint set that is satisfied when `ty` is a `subtype`_ of Returns a constraint set that is satisfied when `ty` is a `subtype`_ of