From fa7798ddd941493d25ef964be4778658672be095 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 29 Aug 2025 09:46:48 -0700 Subject: [PATCH] [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. --- crates/ty_python_semantic/src/types.rs | 12 +++++++----- crates/ty_python_semantic/src/types/type_ordering.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 4afbef3181..eb0fdc7033 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -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), } diff --git a/crates/ty_python_semantic/src/types/type_ordering.rs b/crates/ty_python_semantic/src/types/type_ordering.rs index d1262802e9..ccfe598091 100644 --- a/crates/ty_python_semantic/src/types/type_ordering.rs +++ b/crates/ty_python_semantic/src/types/type_ordering.rs @@ -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, } }