[ty] minor TypedDict fixes (#20146)

## Summary

In `is_disjoint_from_impl`, we should unpack type aliases before we
check `TypedDict`. This change probably doesn't have any visible effect
until we have a more discriminating implementation of disjointness for
`TypedDict`, but making the change now can avoid some confusion/bugs in
future.

In `type_ordering.rs`, we should order `TypedDict` near more similar
types, and leave Union/Intersection together at the end of the list.
This is not necessary for correctness, but it's more consistent and it
could have saved me some confusion trying to figure out why I was only
getting an unreachable panic when my code example included a `TypedDict`
type.

## Test Plan

None besides existing tests.
This commit is contained in:
Carl Meyer 2025-08-29 09:46:48 -07:00 committed by GitHub
parent 8223fea062
commit fa7798ddd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 13 additions and 11 deletions

View file

@ -1986,11 +1986,6 @@ impl<'db> Type<'db> {
(Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => C::unsatisfiable(db),
(Type::TypedDict(_), _) | (_, Type::TypedDict(_)) => {
// TODO: Implement disjointness for TypedDict
C::unsatisfiable(db)
}
(Type::TypeAlias(alias), _) => {
let self_alias_ty = alias.value_type(db);
visitor.visit((self_alias_ty, other), || {
@ -2005,6 +2000,11 @@ impl<'db> Type<'db> {
})
}
(Type::TypedDict(_), _) | (_, Type::TypedDict(_)) => {
// TODO: Implement disjointness for TypedDict
C::unsatisfiable(db)
}
// A typevar is never disjoint from itself, since all occurrences of the typevar must
// be specialized to the same type. (This is an important difference between typevars
// and `Any`!) Different typevars might be disjoint, depending on their bounds and
@ -5924,6 +5924,8 @@ impl<'db> Type<'db> {
Type::AlwaysTruthy | Type::AlwaysFalsy => KnownClass::Type.to_instance(db),
Type::BoundSuper(_) => KnownClass::Super.to_class_literal(db),
Type::ProtocolInstance(protocol) => protocol.to_meta_type(db),
// `TypedDict` instances are instances of `dict` at runtime, but its important that we
// understand a more specific meta type in order to correctly handle `__getitem__`.
Type::TypedDict(typed_dict) => SubclassOfType::from(db, typed_dict.defining_class()),
Type::TypeAlias(alias) => alias.value_type(db).to_meta_type(db),
}

View file

@ -212,6 +212,12 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
(Type::TypeAlias(_), _) => Ordering::Less,
(_, Type::TypeAlias(_)) => Ordering::Greater,
(Type::TypedDict(left), Type::TypedDict(right)) => {
left.defining_class().cmp(&right.defining_class())
}
(Type::TypedDict(_), _) => Ordering::Less,
(_, Type::TypedDict(_)) => Ordering::Greater,
(Type::Union(_), _) | (_, Type::Union(_)) => {
unreachable!("our type representation does not permit nested unions");
}
@ -243,12 +249,6 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
unreachable!("Two equal, normalized intersections should share the same Salsa ID")
}
(Type::TypedDict(left), Type::TypedDict(right)) => {
left.defining_class().cmp(&right.defining_class())
}
(Type::TypedDict(_), _) => Ordering::Less,
(_, Type::TypedDict(_)) => Ordering::Greater,
}
}