[ty] Only consider fully static pivots when deriving transitive constraints (#22444)

When working with constraint sets, we track transitive relationships
between the constraints in the set. For instance, in `S ≤ int ∧ int ≤
T`, we can infer that `S ≤ T`. However, we should only consider fully
static types when looking for a "pivot" for this kind of transitive
relationship. The same pattern does not hold for `S ≤ Any ∧ Any ≤ T`;
because the two `Any`s can materialize to different types, we cannot
infer that `S ≤ T`.

Fixes https://github.com/astral-sh/ty/issues/2371
This commit is contained in:
Douglas Creager
2026-01-08 09:31:55 -05:00
committed by GitHub
parent eea9ad8352
commit 701f5134ab
3 changed files with 82 additions and 6 deletions

View File

@@ -3094,18 +3094,29 @@ impl<'db> SequentMap<'db> {
// (CL ≤ C ≤ pivot) ∧ (pivot ≤ B ≤ BU) → (CL ≤ C ≤ B)
(constrained_lower, constrained_upper)
if constrained_upper == bound_constraint.lower(db)
&& !constrained_upper.is_never()
&& !constrained_upper.is_object() =>
if !constrained_upper.is_never()
&& !constrained_upper.is_object()
&& constrained_upper
.top_materialization(db)
.is_constraint_set_assignable_to(
db,
bound_constraint.lower(db).bottom_materialization(db),
) =>
{
(constrained_lower, Type::TypeVar(bound_typevar))
}
// (pivot ≤ C ≤ CU) ∧ (BL ≤ B ≤ pivot) → (B ≤ C ≤ CU)
(constrained_lower, constrained_upper)
if constrained_lower == bound_constraint.upper(db)
&& !constrained_lower.is_never()
&& !constrained_lower.is_object() =>
if !constrained_lower.is_never()
&& !constrained_lower.is_object()
&& bound_constraint
.upper(db)
.top_materialization(db)
.is_constraint_set_assignable_to(
db,
constrained_lower.bottom_materialization(db),
) =>
{
(Type::TypeVar(bound_typevar), constrained_upper)
}