From 6b7f5d42d28452e99325b3556ea20e60f7f07fbc Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Fri, 12 Dec 2025 13:07:36 +0900 Subject: [PATCH] add `TypedDictType::structural_ordering` --- .../src/types/type_ordering.rs | 4 +- .../src/types/typed_dict.rs | 52 ++++++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/crates/ty_python_semantic/src/types/type_ordering.rs b/crates/ty_python_semantic/src/types/type_ordering.rs index dea72fa601..da7f498b2e 100644 --- a/crates/ty_python_semantic/src/types/type_ordering.rs +++ b/crates/ty_python_semantic/src/types/type_ordering.rs @@ -207,9 +207,7 @@ pub(super) fn structural_type_ordering<'db>( (Type::TypeAlias(_), _) => Ordering::Less, (_, Type::TypeAlias(_)) => Ordering::Greater, - (Type::TypedDict(left), Type::TypedDict(right)) => left - .defining_class() - .structural_ordering(db, right.defining_class()), + (Type::TypedDict(left), Type::TypedDict(right)) => left.structural_ordering(db, *right), (Type::TypedDict(_), _) => Ordering::Less, (_, Type::TypedDict(_)) => Ordering::Greater, diff --git a/crates/ty_python_semantic/src/types/typed_dict.rs b/crates/ty_python_semantic/src/types/typed_dict.rs index dee49a3afe..c8b6ea19b1 100644 --- a/crates/ty_python_semantic/src/types/typed_dict.rs +++ b/crates/ty_python_semantic/src/types/typed_dict.rs @@ -22,7 +22,7 @@ use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::generics::InferableTypeVars; use crate::types::{ HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeContext, - TypeRelation, + TypeRelation, structural_type_ordering, }; use ordermap::OrderSet; @@ -341,6 +341,56 @@ impl<'db> TypedDictType<'db> { }, ) } + + pub(super) fn structural_ordering( + self, + db: &'db dyn Db, + other: TypedDictType<'db>, + ) -> std::cmp::Ordering { + match (self, other) { + (TypedDictType::Class(self_class), TypedDictType::Class(other_class)) => { + self_class.structural_ordering(db, other_class) + } + (TypedDictType::Synthesized(self_dict), TypedDictType::Synthesized(other_dict)) => { + let self_items = self_dict.items(db); + let other_items = other_dict.items(db); + + // Compare number of items first + let items_count = self_items.len().cmp(&other_items.len()); + if items_count != std::cmp::Ordering::Equal { + return items_count; + } + + // Compare each item in order + for ((self_name, self_field), (other_name, other_field)) in + self_items.iter().zip(other_items.iter()) + { + let ord = self_name.cmp(other_name); + if ord != std::cmp::Ordering::Equal { + return ord; + } + + let ord = structural_type_ordering( + db, + &self_field.declared_ty, + &other_field.declared_ty, + ); + if ord != std::cmp::Ordering::Equal { + return ord; + } + + let ord = self_field.flags.bits().cmp(&other_field.flags.bits()); + if ord != std::cmp::Ordering::Equal { + return ord; + } + } + + std::cmp::Ordering::Equal + } + (TypedDictType::Class(_), TypedDictType::Synthesized(_)) => std::cmp::Ordering::Less, + (TypedDictType::Synthesized(_), TypedDictType::Class(_)) => std::cmp::Ordering::Greater, + } + } } pub(crate) fn walk_typed_dict_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(