diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/satisfied_by_all_typevars.md b/crates/ty_python_semantic/resources/mdtest/type_properties/satisfied_by_all_typevars.md index 865cfa8395..346b22a130 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/satisfied_by_all_typevars.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/satisfied_by_all_typevars.md @@ -60,29 +60,29 @@ class Sub(Base): ... class Unrelated: ... 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(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()) # (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). static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) # (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). static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) # (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). static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) # (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). static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars()) ``` @@ -106,38 +106,38 @@ class Sub(Base): ... class Unrelated: ... 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(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()) # (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 # specialization also satisfies (T ≤ Super). static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) # (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). static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) # (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). static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars()) # (T = Never) is a valid specialization, which satisfies (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). 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 # valid specialization that satisfies (T ≤ Unrelated ∧ 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()) ``` @@ -153,15 +153,15 @@ the constraint set. from typing import 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(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()) # If we choose Base as the materialization for the upper bound, then (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()) # 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 # 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, # which satisfies (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, # which satisfies (T ≤ Unrelated). 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, # which satisfies (T ≤ Unrelated ∧ 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 # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # (T ≤ Unrelated ∧ T ≠ Never). @@ -196,15 +196,15 @@ restrictive variance (i.e., invariance), but we get the same results for other v ```py 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(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()) # 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]). - 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 # (T ≤ list[Base]). # 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 # specialization, which satisfies (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 # (T ≤ list[Unrelated]). 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 # specialization, which satisfies (T ≤ list[Unrelated] ∧ 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 # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # (T ≤ list[Unrelated] ∧ T ≠ Never). @@ -251,61 +251,61 @@ class Sub(Base): ... class 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(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()) # (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). static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars()) # (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). static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars()) # (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). static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) # 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()) # (T = Base) and (T = Unrelated) both satisfy (T ≤ Super ∨ 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()) # (T = Base) and (T = Unrelated) both satisfy (T ≤ Base ∨ 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()) # (T = Unrelated) is a valid specialization, which satisfies (T ≤ Sub ∨ 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). static_assert(not constraints.satisfied_by_all_typevars()) # (T = Unrelated) is a valid specialization, which satisfies (T = Super ∨ 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). static_assert(not constraints.satisfied_by_all_typevars()) # (T = Base) and (T = Unrelated) both satisfy (T = Base ∨ 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()) # (T = Unrelated) is a valid specialization, which satisfies (T = Sub ∨ 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). static_assert(not constraints.satisfied_by_all_typevars()) ``` @@ -322,50 +322,50 @@ satisfy the constraint set. from typing import 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(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()) # If we choose Unrelated as the materialization of the gradual constraint, then (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 # not satisfy (T ≤ Unrelated). 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 # 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 # specializations, both of which satisfy (T ≤ Super). 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 # 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 # specializations, both of which satisfy (T ≤ Base). static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars()) 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(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()) # If we choose Unrelated as the materialization of either constraint, then (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()) # If we choose Unrelated as the materialization of both constraints, then (T = Unrelated) is the # only valid specialization, which satisfies (T ≤ Unrelated). 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 # 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 # valid specialization, which satisfies (T ≤ Base). 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 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(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()) # 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 # 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()) # If we choose list[Super] as the materialization, then (T = list[Super]) is a valid # 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 # does not satisfy (T ≤ list[Super]). 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 # 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 # (T ≤ list[Base]). 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, # 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 # does not satisfy (T ≤ list[Sub]). 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 # specialization, which satisfies (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 # does not satisfy (T ≤ list[Unrelated]). 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 # specialization, which satisfies (T ≤ list[Unrelated] ∧ 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 # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # (T ≤ list[Unrelated] ∧ T ≠ Never). static_assert(not constraints.satisfied_by_all_typevars()) 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(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()) # 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 # 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()) # If we choose list[Super] as the materialization, then (T = list[Super]) is a valid # 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 # does not satisfy (T ≤ list[Super]). 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 # 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 # (T ≤ list[Base]). 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, # 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 # does not satisfy (T ≤ list[Sub]). 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 # specialization, which satisfies (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 # does not satisfy (T ≤ list[Unrelated]). 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 # specialization, which satisfies (T ≤ list[Unrelated] ∧ 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 # position. (T = Never) will be a valid assignment no matter what, and that does not satisfy # (T ≤ list[Unrelated] ∧ T ≠ Never). diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0d41d88eb2..2bbb1a1eb5 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -4317,6 +4317,14 @@ impl<'db> Type<'db> { )) .into() } + Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked)) + if name == "with_inferable" => + { + Place::bound(Type::KnownBoundMethod( + KnownBoundMethodType::ConstraintSetWithInferable(tracked), + )) + .into() + } Type::KnownInstance(KnownInstanceType::ConstraintSet(tracked)) if name == "implies_subtype_of" => { @@ -7162,6 +7170,7 @@ impl<'db> Type<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) @@ -7322,6 +7331,7 @@ impl<'db> Type<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), @@ -10780,6 +10790,7 @@ pub enum KnownBoundMethodType<'db> { ConstraintSetRange, ConstraintSetAlways, ConstraintSetNever, + ConstraintSetWithInferable(TrackedConstraintSet<'db>), ConstraintSetImpliesSubtypeOf(TrackedConstraintSet<'db>), ConstraintSetSatisfies(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::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => {} @@ -10877,6 +10889,10 @@ impl<'db> KnownBoundMethodType<'db> { KnownBoundMethodType::ConstraintSetNever, KnownBoundMethodType::ConstraintSetNever, ) + | ( + KnownBoundMethodType::ConstraintSetWithInferable(_), + KnownBoundMethodType::ConstraintSetWithInferable(_), + ) | ( KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_), @@ -10900,6 +10916,7 @@ impl<'db> KnownBoundMethodType<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), @@ -10912,6 +10929,7 @@ impl<'db> KnownBoundMethodType<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), @@ -10965,6 +10983,10 @@ impl<'db> KnownBoundMethodType<'db> { ) => ConstraintSet::from(true), ( + KnownBoundMethodType::ConstraintSetWithInferable(left_constraints), + KnownBoundMethodType::ConstraintSetWithInferable(right_constraints), + ) + | ( KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(left_constraints), KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(right_constraints), ) @@ -10989,6 +11011,7 @@ impl<'db> KnownBoundMethodType<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), @@ -11001,6 +11024,7 @@ impl<'db> KnownBoundMethodType<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_), @@ -11027,6 +11051,7 @@ impl<'db> KnownBoundMethodType<'db> { | KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => self, @@ -11045,6 +11070,7 @@ impl<'db> KnownBoundMethodType<'db> { KnownBoundMethodType::ConstraintSetRange | KnownBoundMethodType::ConstraintSetAlways | KnownBoundMethodType::ConstraintSetNever + | KnownBoundMethodType::ConstraintSetWithInferable(_) | KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) | KnownBoundMethodType::ConstraintSetSatisfies(_) | 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(_) => { Either::Right(std::iter::once(Signature::new( Parameters::new([ @@ -11199,13 +11234,7 @@ impl<'db> KnownBoundMethodType<'db> { KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => { Either::Right(std::iter::once(Signature::new( - Parameters::new([Parameter::keyword_only(Name::new_static("inferable")) - .type_form() - .with_annotated_type(UnionType::from_elements( - db, - [Type::homogeneous_tuple(db, Type::any()), Type::none(db)], - )) - .with_default_type(Type::none(db))]), + Parameters::empty(), Some(KnownClass::Bool.to_instance(db)), ))) } diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 08c4bf80b1..0e2b6db22a 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -36,9 +36,9 @@ use crate::types::tuple::{TupleLength, TupleType}; use crate::types::{ BoundMethodType, BoundTypeVarIdentity, ClassLiteral, DataclassFlags, DataclassParams, FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, - NominalInstanceType, PropertyInstanceType, SpecialFormType, TrackedConstraintSet, - TypeAliasType, TypeContext, UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, - infer_isolated_expression, todo_type, + PropertyInstanceType, SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext, + UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, infer_isolated_expression, + todo_type, }; use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity}; 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: // @@ -300,7 +300,12 @@ impl<'db> Bindings<'db> { /// 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. - 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>, default: bool| -> bool { if let Some(Type::BooleanLiteral(value)) = ty { *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( KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(tracked), ) => { @@ -1214,36 +1246,7 @@ impl<'db> Bindings<'db> { Type::KnownBoundMethod( KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(tracked), ) => { - let extract_inferable = |instance: &NominalInstanceType<'db>| { - if instance.has_known_class(db, KnownClass::NoneType) { - // Caller explicitly passed None, so no typevars are inferable. - return Some(InferableTypeVars::none()); - } - Some(InferableTypeVars::from_bound_typevars( - db, - instance.tuple_spec(db)?.fixed_elements().filter_map(|ty| { - ty.as_typevar() - .map(|bound_typevar| bound_typevar.identity(db)) - }), - )) - }; - - let inferable = match overload.parameter_types() { - // Caller did not provide argument, so no typevars are inferable. - [None] => InferableTypeVars::none(), - [Some(Type::NominalInstance(instance))] => { - match extract_inferable(instance) { - Some(inferable) => inferable, - None => continue, - } - } - _ => continue, - }; - - let result = tracked - .constraints(db) - .with_inferable(inferable) - .satisfied_by_all_typevars(db); + let result = tracked.constraints(db).satisfied_by_all_typevars(db); overload.set_return_type(Type::BooleanLiteral(result)); } diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index b8a8a05ac4..595d194bdc 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -532,6 +532,9 @@ impl Display for DisplayRepresentation<'_> { Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetNever) => { f.write_str("bound method `ConstraintSet.never`") } + Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetWithInferable(_)) => { + f.write_str("bound method `ConstraintSet.with_inferable`") + } Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)) => { f.write_str("bound method `ConstraintSet.implies_subtype_of`") } diff --git a/crates/ty_vendored/ty_extensions/ty_extensions.pyi b/crates/ty_vendored/ty_extensions/ty_extensions.pyi index 744bd5af37..85ef11535a 100644 --- a/crates/ty_vendored/ty_extensions/ty_extensions.pyi +++ b/crates/ty_vendored/ty_extensions/ty_extensions.pyi @@ -59,6 +59,12 @@ class ConstraintSet: def never() -> Self: """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: """ Returns a constraint set that is satisfied when `ty` is a `subtype`_ of