Skip to content

Commit 4b4fc92

Browse files
committed
move typevar arms around
1 parent 2ff0bc4 commit 4b4fc92

File tree

3 files changed

+56
-75
lines changed

3 files changed

+56
-75
lines changed

crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ T = TypeVar("T", int, str)
279279

280280
def same_constrained_types(t1: T, t2: T) -> T:
281281
# TODO: no error
282-
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
282+
# error: [invalid-return-type] "Return type does not match returned value: expected `T@same_constrained_types`, found `int | Unknown`"
283283
return t1 + t2
284284
```
285285

crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ methods that are compatible with the return type, so the `return` expression is
246246
```py
247247
def same_constrained_types[T: (int, str)](t1: T, t2: T) -> T:
248248
# TODO: no error
249-
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
249+
# error: [invalid-return-type] "Return type does not match returned value: expected `T@same_constrained_types`, found `int | Unknown`"
250250
return t1 + t2
251251
```
252252

crates/ty_python_semantic/src/types.rs

Lines changed: 54 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,27 +1457,56 @@ impl<'db> Type<'db> {
14571457
Type::NonInferableTypeVar(rhs_bound_typevar),
14581458
) if lhs_bound_typevar == rhs_bound_typevar => C::always_satisfiable(db),
14591459

1460-
// A fully static typevar is a subtype of its upper bound, and to something similar to
1461-
// the union of its constraints. An unbound, unconstrained, fully static typevar has an
1462-
// implicit upper bound of `object` (which is handled above).
1463-
(Type::NonInferableTypeVar(bound_typevar), _)
1464-
if bound_typevar.typevar(db).bound_or_constraints(db).is_some() =>
1465-
{
1460+
(Type::Union(union), _) => union.elements(db).iter().when_all(db, |&elem_ty| {
1461+
elem_ty.has_relation_to_impl(db, target, relation, visitor)
1462+
}),
1463+
1464+
(_, Type::Union(union)) => union.elements(db).iter().when_any(db, |&elem_ty| {
1465+
self.has_relation_to_impl(db, elem_ty, relation, visitor)
1466+
}),
1467+
1468+
// If both sides are intersections we need to handle the right side first
1469+
// (A & B & C) is a subtype of (A & B) because the left is a subtype of both A and B,
1470+
// but none of A, B, or C is a subtype of (A & B).
1471+
(_, Type::Intersection(intersection)) => (intersection.positive(db).iter())
1472+
.when_all(db, |&pos_ty| {
1473+
self.has_relation_to_impl(db, pos_ty, relation, visitor)
1474+
})
1475+
.and(db, || {
1476+
intersection
1477+
.negative(db)
1478+
.iter()
1479+
.when_all(db, |&neg_ty| self.when_disjoint_from(db, neg_ty))
1480+
}),
1481+
(Type::Intersection(intersection), _) => {
1482+
intersection.positive(db).iter().when_any(db, |&elem_ty| {
1483+
elem_ty.has_relation_to_impl(db, target, relation, visitor)
1484+
})
1485+
}
1486+
1487+
// A non-inferable typevar must satisfy the relation for every possible specialization.
1488+
(Type::NonInferableTypeVar(bound_typevar), _) => {
14661489
match bound_typevar.typevar(db).bound_or_constraints(db) {
1467-
None => unreachable!(),
14681490
// Verify that the upper bound satisfies the relation. (If it does, than any
1469-
// subtype of the upper bound will too.) Also add a safety constraint that
1470-
// ensures that the typevar specializes to a subtype of the upper bound.
1491+
// subtype of the upper bound will too.)
1492+
None => {
1493+
let bound = Type::object(db);
1494+
C::constrain_typevar(db, bound_typevar, Type::Never, bound)
1495+
.implies(db, || {
1496+
bound.has_relation_to_impl(db, target, relation, visitor)
1497+
})
1498+
}
14711499
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
14721500
C::constrain_typevar(db, bound_typevar, Type::Never, bound)
14731501
.implies(db, || {
14741502
bound.has_relation_to_impl(db, target, relation, visitor)
14751503
})
14761504
}
1505+
// Verify that each constraint satisfies the relation. (Note that constrained
1506+
// typevars must specialize to _exactly_ one of the constraints, not to a
1507+
// _subtype_ of one.)
14771508
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
14781509
constraints.elements(db).iter().when_all(db, |constraint| {
1479-
// Note that constrained typevars must specialize to _exactly_ one of the
1480-
// constraints, not to a _subtype_ of one.
14811510
C::constrain_typevar(db, bound_typevar, *constraint, *constraint)
14821511
.implies(db, || {
14831512
constraint.has_relation_to_impl(db, target, relation, visitor)
@@ -1487,79 +1516,31 @@ impl<'db> Type<'db> {
14871516
}
14881517
}
14891518

1490-
// If the typevar is constrained, there must be multiple constraints, and the typevar
1491-
// might be specialized to any one of them. However, the constraints do not have to be
1492-
// disjoint, which means an lhs type might be a subtype of all of the constraints.
1493-
(_, Type::NonInferableTypeVar(bound_typevar))
1494-
if !bound_typevar
1495-
.typevar(db)
1496-
.constraints(db)
1497-
.when_some_and(db, |constraints| {
1498-
constraints.iter().when_all(db, |constraint| {
1499-
C::constrain_typevar(db, bound_typevar, *constraint, *constraint)
1500-
.implies(db, || {
1501-
self.has_relation_to_impl(db, *constraint, relation, visitor)
1502-
})
1503-
})
1504-
})
1505-
.is_never_satisfied(db) =>
1506-
{
1507-
// TODO: The repetition here isn't great, but we really need the fallthrough logic,
1508-
// where this arm only engages if it returns true (or in the world of constraints,
1509-
// not false). Once we're using real constraint sets instead of bool, we should be
1510-
// able to simplify the typevar logic.
1511-
bound_typevar
1512-
.typevar(db)
1513-
.constraints(db)
1514-
.when_some_and(db, |constraints| {
1515-
constraints.iter().when_all(db, |constraint| {
1519+
(_, Type::NonInferableTypeVar(bound_typevar)) => {
1520+
match bound_typevar.typevar(db).bound_or_constraints(db) {
1521+
// There is no guarantee which type an unconstrained typevar might specialize
1522+
// to. Never (which is checked above) is the only type that is a subtype of any
1523+
// specialization.
1524+
None | Some(TypeVarBoundOrConstraints::UpperBound(_)) => C::unsatisfiable(db),
1525+
1526+
// A constrained typevar, on the other hand, does have a fixed set of types
1527+
// that it can specialize to, so we can check them all exhaustively.
1528+
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
1529+
constraints.elements(db).iter().when_all(db, |constraint| {
15161530
C::constrain_typevar(db, bound_typevar, *constraint, *constraint)
15171531
.implies(db, || {
15181532
self.has_relation_to_impl(db, *constraint, relation, visitor)
15191533
})
15201534
})
1521-
})
1535+
}
1536+
}
15221537
}
15231538

15241539
// `Never` is the bottom type, the empty set.
15251540
// Other than one unlikely edge case (TypeVars bound to `Never`),
15261541
// no other type is a subtype of or assignable to `Never`.
15271542
(_, Type::Never) => C::unsatisfiable(db),
15281543

1529-
(Type::Union(union), _) => union.elements(db).iter().when_all(db, |&elem_ty| {
1530-
elem_ty.has_relation_to_impl(db, target, relation, visitor)
1531-
}),
1532-
1533-
(_, Type::Union(union)) => union.elements(db).iter().when_any(db, |&elem_ty| {
1534-
self.has_relation_to_impl(db, elem_ty, relation, visitor)
1535-
}),
1536-
1537-
// If both sides are intersections we need to handle the right side first
1538-
// (A & B & C) is a subtype of (A & B) because the left is a subtype of both A and B,
1539-
// but none of A, B, or C is a subtype of (A & B).
1540-
(_, Type::Intersection(intersection)) => (intersection.positive(db).iter())
1541-
.when_all(db, |&pos_ty| {
1542-
self.has_relation_to_impl(db, pos_ty, relation, visitor)
1543-
})
1544-
.and(db, || {
1545-
intersection
1546-
.negative(db)
1547-
.iter()
1548-
.when_all(db, |&neg_ty| self.when_disjoint_from(db, neg_ty))
1549-
}),
1550-
(Type::Intersection(intersection), _) => {
1551-
intersection.positive(db).iter().when_any(db, |&elem_ty| {
1552-
elem_ty.has_relation_to_impl(db, target, relation, visitor)
1553-
})
1554-
}
1555-
1556-
// Other than the special cases checked above, no other types are a subtype of a
1557-
// typevar, since there's no guarantee what type the typevar will be specialized to.
1558-
// (If the typevar is bounded, it might be specialized to a smaller type than the
1559-
// bound. This is true even if the bound is a final class, since the typevar can still
1560-
// be specialized to `Never`.)
1561-
(_, Type::NonInferableTypeVar(_)) => C::unsatisfiable(db),
1562-
15631544
// TODO: Infer specializations here
15641545
(Type::TypeVar(_), _) | (_, Type::TypeVar(_)) => C::unsatisfiable(db),
15651546

@@ -1822,7 +1803,7 @@ impl<'db> Type<'db> {
18221803

18231804
// Other than the special cases enumerated above, `Instance` types and typevars are
18241805
// never subtypes of any other variants
1825-
(Type::NominalInstance(_) | Type::NonInferableTypeVar(_), _) => C::unsatisfiable(db),
1806+
(Type::NominalInstance(_), _) => C::unsatisfiable(db),
18261807
}
18271808
}
18281809

0 commit comments

Comments
 (0)