mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
move typevar arms around
This commit is contained in:
parent
19c421a27b
commit
b1a987318f
3 changed files with 71 additions and 93 deletions
|
@ -279,7 +279,7 @@ T = TypeVar("T", int, str)
|
|||
|
||||
def same_constrained_types(t1: T, t2: T) -> T:
|
||||
# TODO: no error
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T@same_constrained_types`, found `int | Unknown`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
|
@ -246,7 +246,7 @@ methods that are compatible with the return type, so the `return` expression is
|
|||
```py
|
||||
def same_constrained_types[T: (int, str)](t1: T, t2: T) -> T:
|
||||
# TODO: no error
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T@same_constrained_types`, found `int | Unknown`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
|
@ -1568,85 +1568,6 @@ impl<'db> Type<'db> {
|
|||
Type::NonInferableTypeVar(rhs_bound_typevar),
|
||||
) if lhs_bound_typevar == rhs_bound_typevar => ConstraintSet::from(true),
|
||||
|
||||
// A fully static typevar is a subtype of its upper bound, and to something similar to
|
||||
// the union of its constraints. An unbound, unconstrained, fully static typevar has an
|
||||
// implicit upper bound of `object` (which is handled above).
|
||||
(Type::NonInferableTypeVar(bound_typevar), _)
|
||||
if bound_typevar.typevar(db).bound_or_constraints(db).is_some() =>
|
||||
{
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => unreachable!(),
|
||||
// Verify that the upper bound satisfies the relation. (If it does, than any
|
||||
// subtype of the upper bound will too.) Also add a safety constraint that
|
||||
// ensures that the typevar specializes to a subtype of the upper bound.
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
ConstraintSet::constrain_typevar(db, bound_typevar, Type::Never, bound)
|
||||
.implies(db, || {
|
||||
bound.has_relation_to_impl(db, target, relation, visitor)
|
||||
})
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
constraints.elements(db).iter().when_all(db, |constraint| {
|
||||
// Note that constrained typevars must specialize to _exactly_ one of the
|
||||
// constraints, not to a _subtype_ of one.
|
||||
ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
bound_typevar,
|
||||
*constraint,
|
||||
*constraint,
|
||||
)
|
||||
.implies(db, || {
|
||||
constraint.has_relation_to_impl(db, target, relation, visitor)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the typevar is constrained, there must be multiple constraints, and the typevar
|
||||
// might be specialized to any one of them. However, the constraints do not have to be
|
||||
// disjoint, which means an lhs type might be a subtype of all of the constraints.
|
||||
(_, Type::NonInferableTypeVar(bound_typevar))
|
||||
if !bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.when_some_and(|constraints| {
|
||||
constraints.iter().when_all(db, |constraint| {
|
||||
ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
bound_typevar,
|
||||
*constraint,
|
||||
*constraint,
|
||||
)
|
||||
.implies(db, || {
|
||||
self.has_relation_to_impl(db, *constraint, relation, visitor)
|
||||
})
|
||||
})
|
||||
})
|
||||
.is_never_satisfied() =>
|
||||
{
|
||||
// TODO: The repetition here isn't great, but we really need the fallthrough logic,
|
||||
// where this arm only engages if it returns true (or in the world of constraints,
|
||||
// not false). Once we're using real constraint sets instead of bool, we should be
|
||||
// able to simplify the typevar logic.
|
||||
bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.when_some_and(|constraints| {
|
||||
constraints.iter().when_all(db, |constraint| {
|
||||
ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
bound_typevar,
|
||||
*constraint,
|
||||
*constraint,
|
||||
)
|
||||
.implies(db, || {
|
||||
self.has_relation_to_impl(db, *constraint, relation, visitor)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
(Type::TypeVar(_), _) if relation.is_assignability() => {
|
||||
// The implicit lower bound of a typevar is `Never`, which means
|
||||
// that it is always assignable to any other type.
|
||||
|
@ -1656,9 +1577,6 @@ impl<'db> Type<'db> {
|
|||
ConstraintSet::from(true)
|
||||
}
|
||||
|
||||
// `Never` is the bottom type, the empty set.
|
||||
(_, Type::Never) => ConstraintSet::from(false),
|
||||
|
||||
(Type::Union(union), _) => union.elements(db).iter().when_all(db, |&elem_ty| {
|
||||
elem_ty.has_relation_to_impl(db, target, relation, visitor)
|
||||
}),
|
||||
|
@ -1686,12 +1604,74 @@ impl<'db> Type<'db> {
|
|||
})
|
||||
}
|
||||
|
||||
// Other than the special cases checked above, no other types are a subtype of a
|
||||
// typevar, since there's no guarantee what type the typevar will be specialized to.
|
||||
// (If the typevar is bounded, it might be specialized to a smaller type than the
|
||||
// bound. This is true even if the bound is a final class, since the typevar can still
|
||||
// be specialized to `Never`.)
|
||||
(_, Type::NonInferableTypeVar(_)) => ConstraintSet::from(false),
|
||||
// A non-inferable typevar must satisfy the relation for every possible specialization.
|
||||
(Type::NonInferableTypeVar(bound_typevar), _) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
// Verify that the upper bound satisfies the relation. (If it does, than any
|
||||
// subtype of the upper bound will too.)
|
||||
None => {
|
||||
let bound = Type::object();
|
||||
ConstraintSet::constrain_typevar(db, bound_typevar, Type::Never, bound)
|
||||
.implies(db, || {
|
||||
bound.has_relation_to_impl(db, target, relation, visitor)
|
||||
})
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
ConstraintSet::constrain_typevar(db, bound_typevar, Type::Never, bound)
|
||||
.implies(db, || {
|
||||
bound.has_relation_to_impl(db, target, relation, visitor)
|
||||
})
|
||||
}
|
||||
// Verify that each constraint satisfies the relation. (Note that constrained
|
||||
// typevars must specialize to _exactly_ one of the constraints, not to a
|
||||
// _subtype_ of one.)
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
constraints.elements(db).iter().when_all(db, |constraint| {
|
||||
ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
bound_typevar,
|
||||
*constraint,
|
||||
*constraint,
|
||||
)
|
||||
.implies(db, || {
|
||||
constraint.has_relation_to_impl(db, target, relation, visitor)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(_, Type::NonInferableTypeVar(bound_typevar)) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
// There is no guarantee which type an unconstrained typevar might specialize
|
||||
// to. Never (which is checked above) is the only type that is a subtype of any
|
||||
// specialization.
|
||||
None | Some(TypeVarBoundOrConstraints::UpperBound(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
// A constrained typevar, on the other hand, does have a fixed set of types
|
||||
// that it can specialize to, so we can check them all exhaustively.
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
constraints.elements(db).iter().when_all(db, |constraint| {
|
||||
ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
bound_typevar,
|
||||
*constraint,
|
||||
*constraint,
|
||||
)
|
||||
.implies(db, || {
|
||||
self.has_relation_to_impl(db, *constraint, relation, visitor)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `Never` is the bottom type, the empty set.
|
||||
// Other than one unlikely edge case (TypeVars bound to `Never`),
|
||||
// no other type is a subtype of or assignable to `Never`.
|
||||
(_, Type::Never) => ConstraintSet::from(false),
|
||||
|
||||
(_, Type::TypeVar(typevar))
|
||||
if relation.is_assignability()
|
||||
|
@ -1968,9 +1948,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
// Other than the special cases enumerated above, `Instance` types and typevars are
|
||||
// never subtypes of any other variants
|
||||
(Type::NominalInstance(_) | Type::NonInferableTypeVar(_), _) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
(Type::NominalInstance(_), _) => ConstraintSet::from(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue