mirror of https://github.com/astral-sh/ruff
[ty] `type[T]` is assignable to an inferable typevar (#21766)
## Summary Resolves https://github.com/astral-sh/ty/issues/1712.
This commit is contained in:
parent
2250fa6f98
commit
7b0aab1696
|
|
@ -227,6 +227,17 @@ def _[T: (int | str, int)](_: T):
|
|||
static_assert(not is_disjoint_from(type[int], type[T]))
|
||||
```
|
||||
|
||||
```py
|
||||
class X[T]:
|
||||
value: T
|
||||
|
||||
def get(self) -> T:
|
||||
return self.value
|
||||
|
||||
def _[T](x: X[type[T]]):
|
||||
reveal_type(x.get()) # revealed: type[T@_]
|
||||
```
|
||||
|
||||
## Generic Type Inference
|
||||
|
||||
```py
|
||||
|
|
|
|||
|
|
@ -2105,11 +2105,12 @@ impl<'db> Type<'db> {
|
|||
})
|
||||
.is_never_satisfied(db) =>
|
||||
{
|
||||
// TODO: The repetition here isn't great, but we really need the fallthrough logic,
|
||||
// where this arm only engages if it returns true.
|
||||
let this_instance = Type::TypeVar(subclass_of.into_type_var().unwrap());
|
||||
target.to_instance(db).when_some_and(|other_instance| {
|
||||
this_instance.has_relation_to_impl(
|
||||
// TODO: The repetition here isn't great, but we need the fallthrough logic.
|
||||
subclass_of
|
||||
.into_type_var()
|
||||
.zip(target.to_instance(db))
|
||||
.when_some_and(|(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).has_relation_to_impl(
|
||||
db,
|
||||
other_instance,
|
||||
inferable,
|
||||
|
|
@ -2120,12 +2121,30 @@ impl<'db> Type<'db> {
|
|||
})
|
||||
}
|
||||
|
||||
(_, Type::SubclassOf(subclass_of)) if subclass_of.is_type_var() => {
|
||||
let other_instance = Type::TypeVar(subclass_of.into_type_var().unwrap());
|
||||
self.to_instance(db).when_some_and(|this_instance| {
|
||||
(_, Type::SubclassOf(subclass_of))
|
||||
if !subclass_of
|
||||
.into_type_var()
|
||||
.zip(self.to_instance(db))
|
||||
.when_some_and(|(other_instance, this_instance)| {
|
||||
this_instance.has_relation_to_impl(
|
||||
db,
|
||||
other_instance,
|
||||
Type::TypeVar(other_instance),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
.is_never_satisfied(db) =>
|
||||
{
|
||||
// TODO: The repetition here isn't great, but we need the fallthrough logic.
|
||||
subclass_of
|
||||
.into_type_var()
|
||||
.zip(self.to_instance(db))
|
||||
.when_some_and(|(other_instance, this_instance)| {
|
||||
this_instance.has_relation_to_impl(
|
||||
db,
|
||||
Type::TypeVar(other_instance),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
|
|
@ -2656,7 +2675,9 @@ impl<'db> Type<'db> {
|
|||
disjointness_visitor,
|
||||
),
|
||||
|
||||
(Type::SubclassOf(subclass_of), _) if subclass_of.is_type_var() => {
|
||||
(Type::SubclassOf(subclass_of), _) | (_, Type::SubclassOf(subclass_of))
|
||||
if subclass_of.is_type_var() =>
|
||||
{
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
|
|
@ -3116,26 +3137,26 @@ impl<'db> Type<'db> {
|
|||
|
||||
// `type[T]` is disjoint from a class object `A` if every instance of `T` is disjoint from an instance of `A`.
|
||||
(Type::SubclassOf(subclass_of), other) | (other, Type::SubclassOf(subclass_of))
|
||||
if subclass_of.is_type_var()
|
||||
&& (other.to_instance(db).is_some()
|
||||
|| other.as_typevar().is_some_and(|type_var| {
|
||||
type_var.typevar(db).bound_or_constraints(db).is_none()
|
||||
})) =>
|
||||
if !subclass_of
|
||||
.into_type_var()
|
||||
.zip(other.to_instance(db))
|
||||
.when_none_or(|(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).is_disjoint_from_impl(
|
||||
db,
|
||||
other_instance,
|
||||
inferable,
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
)
|
||||
})
|
||||
.is_always_satisfied(db) =>
|
||||
{
|
||||
let this_instance = Type::TypeVar(subclass_of.into_type_var().unwrap());
|
||||
let other_instance = match other {
|
||||
// An unbounded typevar `U` may have instances of type `object` if specialized to
|
||||
// an instance of `type`.
|
||||
Type::TypeVar(typevar)
|
||||
if typevar.typevar(db).bound_or_constraints(db).is_none() =>
|
||||
{
|
||||
Some(Type::object())
|
||||
}
|
||||
_ => other.to_instance(db),
|
||||
};
|
||||
|
||||
other_instance.when_none_or(|other_instance| {
|
||||
this_instance.is_disjoint_from_impl(
|
||||
// TODO: The repetition here isn't great, but we need the fallthrough logic.
|
||||
subclass_of
|
||||
.into_type_var()
|
||||
.zip(other.to_instance(db))
|
||||
.when_none_or(|(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).is_disjoint_from_impl(
|
||||
db,
|
||||
other_instance,
|
||||
inferable,
|
||||
|
|
|
|||
Loading…
Reference in New Issue