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

View File

@ -1460,11 +1460,6 @@ impl<'db> Type<'db> {
.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:
// 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`.
@ -1816,6 +1811,16 @@ impl<'db> Type<'db> {
.unwrap_or_else(|| KnownClass::Type.to_instance(db))
.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)`,
// 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.

View File

@ -64,6 +64,8 @@ pub(crate) enum Ty {
/// where the class has `Any` in its MRO
UnittestMockInstance,
UnittestMockLiteral,
/// A custom `TypedDict`, imported from the `_ssl` module
TypedDict(&'static str),
}
#[derive(Debug, Clone, PartialEq)]
@ -236,6 +238,15 @@ impl Ty {
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",
method: "bit_length",
},
Ty::TypedDict("_Cipher"),
Ty::TypedDict("_CertInfo"),
];
let types = if fully_static {
&types[fully_static_index..]