Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/type_of/generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
125 changes: 73 additions & 52 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2105,33 +2105,52 @@ 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(
db,
other_instance,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
})
// 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,
relation,
relation_visitor,
disjointness_visitor,
)
})
}

(_, 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| {
this_instance.has_relation_to_impl(
db,
other_instance,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
})
(_, 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,
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,
disjointness_visitor,
)
})
}

// A fully static typevar is a subtype of its upper bound, and to something similar to
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -3116,33 +3137,33 @@ 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(
db,
other_instance,
inferable,
disjointness_visitor,
relation_visitor,
)
})
// 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,
disjointness_visitor,
relation_visitor,
)
})
}

// A typevar is never disjoint from itself, since all occurrences of the typevar must
Expand Down
Loading