Skip to content

Commit 60b486a

Browse files
authored
[ty] Deeply normalize many types (#18222)
1 parent 32403df commit 60b486a

File tree

5 files changed

+255
-22
lines changed

5 files changed

+255
-22
lines changed

crates/ty_python_semantic/src/types.rs

Lines changed: 163 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,14 @@ impl<'db> PropertyInstanceType<'db> {
357357
Self::new(db, getter, setter)
358358
}
359359

360+
fn normalized(self, db: &'db dyn Db) -> Self {
361+
Self::new(
362+
db,
363+
self.getter(db).map(|ty| ty.normalized(db)),
364+
self.setter(db).map(|ty| ty.normalized(db)),
365+
)
366+
}
367+
360368
fn find_legacy_typevars(
361369
self,
362370
db: &'db dyn Db,
@@ -1005,28 +1013,17 @@ impl<'db> Type<'db> {
10051013
Type::Callable(callable) => Type::Callable(callable.normalized(db)),
10061014
Type::ProtocolInstance(protocol) => protocol.normalized(db),
10071015
Type::NominalInstance(instance) => Type::NominalInstance(instance.normalized(db)),
1008-
Type::Dynamic(_) => Type::any(),
1009-
Type::LiteralString
1010-
| Type::PropertyInstance(_)
1011-
| Type::AlwaysFalsy
1012-
| Type::AlwaysTruthy
1013-
| Type::BooleanLiteral(_)
1014-
| Type::BytesLiteral(_)
1015-
| Type::StringLiteral(_)
1016-
| Type::Never
1017-
| Type::FunctionLiteral(_)
1018-
| Type::MethodWrapper(_)
1019-
| Type::BoundMethod(_)
1020-
| Type::WrapperDescriptor(_)
1021-
| Type::DataclassDecorator(_)
1022-
| Type::DataclassTransformer(_)
1023-
| Type::ModuleLiteral(_)
1024-
| Type::ClassLiteral(_)
1025-
| Type::KnownInstance(_)
1026-
| Type::IntLiteral(_)
1027-
| Type::BoundSuper(_)
1028-
| Type::SubclassOf(_) => self,
1016+
Type::Dynamic(dynamic) => Type::Dynamic(dynamic.normalized()),
1017+
Type::FunctionLiteral(function) => Type::FunctionLiteral(function.normalized(db)),
1018+
Type::PropertyInstance(property) => Type::PropertyInstance(property.normalized(db)),
1019+
Type::MethodWrapper(method_kind) => Type::MethodWrapper(method_kind.normalized(db)),
1020+
Type::BoundMethod(method) => Type::BoundMethod(method.normalized(db)),
1021+
Type::BoundSuper(bound_super) => Type::BoundSuper(bound_super.normalized(db)),
10291022
Type::GenericAlias(generic) => Type::GenericAlias(generic.normalized(db)),
1023+
Type::SubclassOf(subclass_of) => Type::SubclassOf(subclass_of.normalized(db)),
1024+
Type::KnownInstance(known_instance) => {
1025+
Type::KnownInstance(known_instance.normalized(db))
1026+
}
10301027
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
10311028
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
10321029
Type::TypeVar(TypeVarInstance::new(
@@ -1052,6 +1049,19 @@ impl<'db> Type<'db> {
10521049
}
10531050
None => self,
10541051
},
1052+
Type::LiteralString
1053+
| Type::AlwaysFalsy
1054+
| Type::AlwaysTruthy
1055+
| Type::BooleanLiteral(_)
1056+
| Type::BytesLiteral(_)
1057+
| Type::StringLiteral(_)
1058+
| Type::Never
1059+
| Type::WrapperDescriptor(_)
1060+
| Type::DataclassDecorator(_)
1061+
| Type::DataclassTransformer(_)
1062+
| Type::ModuleLiteral(_)
1063+
| Type::ClassLiteral(_)
1064+
| Type::IntLiteral(_) => self,
10551065
}
10561066
}
10571067

@@ -5604,6 +5614,18 @@ impl<'db> TypeMapping<'_, 'db> {
56045614
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
56055615
}
56065616
}
5617+
5618+
fn normalized(&self, db: &'db dyn Db) -> Self {
5619+
match self {
5620+
TypeMapping::Specialization(specialization) => {
5621+
TypeMapping::Specialization(specialization.normalized(db))
5622+
}
5623+
TypeMapping::PartialSpecialization(partial) => {
5624+
TypeMapping::PartialSpecialization(partial.normalized(db))
5625+
}
5626+
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
5627+
}
5628+
}
56075629
}
56085630

56095631
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
@@ -5627,6 +5649,13 @@ pub enum DynamicType {
56275649
TodoPEP695ParamSpec,
56285650
}
56295651

5652+
impl DynamicType {
5653+
#[expect(clippy::unused_self)]
5654+
fn normalized(self) -> Self {
5655+
Self::Any
5656+
}
5657+
}
5658+
56305659
impl std::fmt::Display for DynamicType {
56315660
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56325661
match self {
@@ -5877,6 +5906,18 @@ impl<'db> TypeVarInstance<'db> {
58775906
None
58785907
}
58795908
}
5909+
5910+
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
5911+
Self::new(
5912+
db,
5913+
self.name(db),
5914+
self.definition(db),
5915+
self.bound_or_constraints(db).map(|b| b.normalized(db)),
5916+
self.variance(db),
5917+
self.default_ty(db).map(|d| d.normalized(db)),
5918+
self.kind(db),
5919+
)
5920+
}
58805921
}
58815922

58825923
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
@@ -5893,6 +5934,19 @@ pub enum TypeVarBoundOrConstraints<'db> {
58935934
Constraints(UnionType<'db>),
58945935
}
58955936

5937+
impl<'db> TypeVarBoundOrConstraints<'db> {
5938+
fn normalized(self, db: &'db dyn Db) -> Self {
5939+
match self {
5940+
TypeVarBoundOrConstraints::UpperBound(bound) => {
5941+
TypeVarBoundOrConstraints::UpperBound(bound.normalized(db))
5942+
}
5943+
TypeVarBoundOrConstraints::Constraints(constraints) => {
5944+
TypeVarBoundOrConstraints::Constraints(constraints.normalized(db))
5945+
}
5946+
}
5947+
}
5948+
}
5949+
58965950
/// Error returned if a type is not (or may not be) a context manager.
58975951
#[derive(Debug)]
58985952
enum ContextManagerError<'db> {
@@ -7123,6 +7177,29 @@ impl<'db> FunctionType<'db> {
71237177
.is_gradual_equivalent_to(db, other.into_callable_type(db))
71247178
}
71257179

7180+
fn normalized(self, db: &'db dyn Db) -> Self {
7181+
let context = self
7182+
.inherited_generic_context(db)
7183+
.map(|ctx| ctx.normalized(db));
7184+
7185+
let mappings: Box<_> = self
7186+
.type_mappings(db)
7187+
.iter()
7188+
.map(|mapping| mapping.normalized(db))
7189+
.collect();
7190+
7191+
Self::new(
7192+
db,
7193+
self.name(db),
7194+
self.known(db),
7195+
self.body_scope(db),
7196+
self.decorators(db),
7197+
self.dataclass_transformer_params(db),
7198+
context,
7199+
mappings,
7200+
)
7201+
}
7202+
71267203
/// Returns a tuple of two spans. The first is
71277204
/// the span for the identifier of the function
71287205
/// definition for `self`. The second is
@@ -7410,6 +7487,14 @@ impl<'db> BoundMethodType<'db> {
74107487
))
74117488
}
74127489

7490+
fn normalized(self, db: &'db dyn Db) -> Self {
7491+
Self::new(
7492+
db,
7493+
self.function(db).normalized(db),
7494+
self.self_instance(db).normalized(db),
7495+
)
7496+
}
7497+
74137498
fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
74147499
// A bound method is a typically a subtype of itself. However, we must explicitly verify
74157500
// the subtyping of the underlying function signatures (since they might be specialized
@@ -7813,6 +7898,24 @@ impl<'db> MethodWrapperKind<'db> {
78137898
) => false,
78147899
}
78157900
}
7901+
7902+
fn normalized(self, db: &'db dyn Db) -> Self {
7903+
match self {
7904+
MethodWrapperKind::FunctionTypeDunderGet(function) => {
7905+
MethodWrapperKind::FunctionTypeDunderGet(function.normalized(db))
7906+
}
7907+
MethodWrapperKind::FunctionTypeDunderCall(function) => {
7908+
MethodWrapperKind::FunctionTypeDunderCall(function.normalized(db))
7909+
}
7910+
MethodWrapperKind::PropertyDunderGet(property) => {
7911+
MethodWrapperKind::PropertyDunderGet(property.normalized(db))
7912+
}
7913+
MethodWrapperKind::PropertyDunderSet(property) => {
7914+
MethodWrapperKind::PropertyDunderSet(property.normalized(db))
7915+
}
7916+
MethodWrapperKind::StrStartswith(_) => self,
7917+
}
7918+
}
78167919
}
78177920

78187921
/// Represents a specific instance of `types.WrapperDescriptorType`
@@ -7907,6 +8010,10 @@ impl<'db> PEP695TypeAliasType<'db> {
79078010
let definition = self.definition(db);
79088011
definition_expression_type(db, definition, &type_alias_stmt_node.value)
79098012
}
8013+
8014+
fn normalized(self, _db: &'db dyn Db) -> Self {
8015+
self
8016+
}
79108017
}
79118018

79128019
/// # Ordering
@@ -7921,17 +8028,35 @@ pub struct BareTypeAliasType<'db> {
79218028
pub value: Type<'db>,
79228029
}
79238030

8031+
impl<'db> BareTypeAliasType<'db> {
8032+
fn normalized(self, db: &'db dyn Db) -> Self {
8033+
Self::new(
8034+
db,
8035+
self.name(db),
8036+
self.definition(db),
8037+
self.value(db).normalized(db),
8038+
)
8039+
}
8040+
}
8041+
79248042
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Update)]
79258043
pub enum TypeAliasType<'db> {
79268044
PEP695(PEP695TypeAliasType<'db>),
79278045
Bare(BareTypeAliasType<'db>),
79288046
}
79298047

79308048
impl<'db> TypeAliasType<'db> {
8049+
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
8050+
match self {
8051+
TypeAliasType::PEP695(type_alias) => TypeAliasType::PEP695(type_alias.normalized(db)),
8052+
TypeAliasType::Bare(type_alias) => TypeAliasType::Bare(type_alias.normalized(db)),
8053+
}
8054+
}
8055+
79318056
pub(crate) fn name(self, db: &'db dyn Db) -> &'db str {
79328057
match self {
79338058
TypeAliasType::PEP695(type_alias) => type_alias.name(db),
7934-
TypeAliasType::Bare(type_alias) => type_alias.name(db).as_str(),
8059+
TypeAliasType::Bare(type_alias) => type_alias.name(db),
79358060
}
79368061
}
79378062

@@ -8595,6 +8720,14 @@ pub enum SuperOwnerKind<'db> {
85958720
}
85968721

85978722
impl<'db> SuperOwnerKind<'db> {
8723+
fn normalized(self, db: &'db dyn Db) -> Self {
8724+
match self {
8725+
SuperOwnerKind::Dynamic(dynamic) => SuperOwnerKind::Dynamic(dynamic.normalized()),
8726+
SuperOwnerKind::Class(class) => SuperOwnerKind::Class(class.normalized(db)),
8727+
SuperOwnerKind::Instance(instance) => SuperOwnerKind::Instance(instance.normalized(db)),
8728+
}
8729+
}
8730+
85988731
fn iter_mro(self, db: &'db dyn Db) -> impl Iterator<Item = ClassBase<'db>> {
85998732
match self {
86008733
SuperOwnerKind::Dynamic(dynamic) => {
@@ -8829,6 +8962,14 @@ impl<'db> BoundSuperType<'db> {
88298962
),
88308963
}
88318964
}
8965+
8966+
fn normalized(self, db: &'db dyn Db) -> Self {
8967+
Self::new(
8968+
db,
8969+
self.pivot_class(db).normalized(db),
8970+
self.owner(db).normalized(db),
8971+
)
8972+
}
88328973
}
88338974

88348975
// Make sure that the `Type` enum does not grow unexpectedly.

crates/ty_python_semantic/src/types/class_base.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ impl<'db> ClassBase<'db> {
3131
Self::Dynamic(DynamicType::Unknown)
3232
}
3333

34+
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
35+
match self {
36+
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
37+
Self::Class(class) => Self::Class(class.normalized(db)),
38+
Self::Protocol(generic_context) => {
39+
Self::Protocol(generic_context.map(|context| context.normalized(db)))
40+
}
41+
Self::Generic(generic_context) => {
42+
Self::Generic(generic_context.map(|context| context.normalized(db)))
43+
}
44+
}
45+
}
46+
3447
pub(crate) fn display(self, db: &'db dyn Db) -> impl std::fmt::Display + 'db {
3548
struct Display<'db> {
3649
base: ClassBase<'db>,

crates/ty_python_semantic/src/types/generics.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ impl<'db> GenericContext<'db> {
237237

238238
Specialization::new(db, self, expanded.into_boxed_slice())
239239
}
240+
241+
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
242+
let variables: FxOrderSet<_> = self
243+
.variables(db)
244+
.iter()
245+
.map(|ty| ty.normalized(db))
246+
.collect();
247+
Self::new(db, variables, self.origin(db))
248+
}
240249
}
241250

242251
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
@@ -561,6 +570,16 @@ impl<'db> PartialSpecialization<'_, 'db> {
561570
types: Cow::from(self.types.clone().into_owned()),
562571
}
563572
}
573+
574+
pub(crate) fn normalized(&self, db: &'db dyn Db) -> PartialSpecialization<'db, 'db> {
575+
let generic_context = self.generic_context.normalized(db);
576+
let types: Cow<_> = self.types.iter().map(|ty| ty.normalized(db)).collect();
577+
578+
PartialSpecialization {
579+
generic_context,
580+
types,
581+
}
582+
}
564583
}
565584

566585
/// Performs type inference between parameter annotations and argument types, producing a

0 commit comments

Comments
 (0)