[ty] Consider a TypedDict type to be a subtype of itself

This commit is contained in:
David Peter 2025-09-04 15:55:24 +02:00
parent b49aa35074
commit 4eaf24028d
3 changed files with 27 additions and 5 deletions

View File

@ -330,6 +330,8 @@ pub enum KnownModule {
UnittestMock, UnittestMock,
#[cfg(test)] #[cfg(test)]
Uuid, Uuid,
#[cfg(test)]
UnderscoreSsl,
Warnings, Warnings,
} }
@ -355,6 +357,8 @@ impl KnownModule {
Self::UnittestMock => "unittest.mock", Self::UnittestMock => "unittest.mock",
#[cfg(test)] #[cfg(test)]
Self::Uuid => "uuid", Self::Uuid => "uuid",
#[cfg(test)]
Self::UnderscoreSsl => "_ssl",
Self::Templatelib => "string.templatelib", Self::Templatelib => "string.templatelib",
} }
} }

View File

@ -1460,11 +1460,6 @@ impl<'db> Type<'db> {
.has_relation_to_impl(db, right, relation, visitor) .has_relation_to_impl(db, right, relation, visitor)
} }
(Type::TypedDict(_), _) | (_, Type::TypedDict(_)) => {
// TODO: Implement assignability and subtyping for TypedDict
C::from_bool(db, relation.is_assignability())
}
// In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied: // In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied:
// 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`. // 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`.
// TypeVars without an explicit upper bound are treated as having an implicit upper bound of `object`. // TypeVars without an explicit upper bound are treated as having an implicit upper bound of `object`.
@ -1816,6 +1811,16 @@ impl<'db> Type<'db> {
.unwrap_or_else(|| KnownClass::Type.to_instance(db)) .unwrap_or_else(|| KnownClass::Type.to_instance(db))
.has_relation_to_impl(db, target, relation, visitor), .has_relation_to_impl(db, target, relation, visitor),
(Type::TypedDict(left), Type::TypedDict(right)) if left == right => {
// TODO: Implement structural assignability and subtyping for TypedDict
C::always_satisfiable(db)
}
(Type::TypedDict(_), _) | (_, Type::TypedDict(_)) => {
// TODO: Implement structural assignability and subtyping for TypedDict
C::from_bool(db, relation.is_assignability())
}
// For example: `Type::SpecialForm(SpecialFormType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`, // For example: `Type::SpecialForm(SpecialFormType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`,
// because `Type::SpecialForm(SpecialFormType::Type)` is a set with exactly one runtime value in it // because `Type::SpecialForm(SpecialFormType::Type)` is a set with exactly one runtime value in it
// (the symbol `typing.Type`), and that symbol is known to be an instance of `typing._SpecialForm` at runtime. // (the symbol `typing.Type`), and that symbol is known to be an instance of `typing._SpecialForm` at runtime.

View File

@ -64,6 +64,8 @@ pub(crate) enum Ty {
/// where the class has `Any` in its MRO /// where the class has `Any` in its MRO
UnittestMockInstance, UnittestMockInstance,
UnittestMockLiteral, UnittestMockLiteral,
/// A custom `TypedDict`, imported from the `_ssl` module
TypedDict(&'static str),
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -236,6 +238,15 @@ impl Ty {
returns.map(|ty| ty.into_type(db)), returns.map(|ty| ty.into_type(db)),
), ),
), ),
Ty::TypedDict(name) => {
let ty = known_module_symbol(db, KnownModule::UnderscoreSsl, name)
.place
.expect_type()
.to_instance(db)
.expect("Class type should be convertible to instance");
assert!(ty.is_typed_dict());
ty
}
} }
} }
} }
@ -314,6 +325,8 @@ fn arbitrary_core_type(g: &mut Gen, fully_static: bool) -> Ty {
class: "int", class: "int",
method: "bit_length", method: "bit_length",
}, },
Ty::TypedDict("_Cipher"),
Ty::TypedDict("_CertInfo"),
]; ];
let types = if fully_static { let types = if fully_static {
&types[fully_static_index..] &types[fully_static_index..]