[ty] Narrow specialized generics using isinstance() (#20256)

Closes astral-sh/ty#456. Part of astral-sh/ty#994.

After all the foundational work, this is only a small change, but let's
see if it exposes any unresolved issues.
This commit is contained in:
Jelle Zijlstra 2025-09-04 15:28:33 -07:00 committed by GitHub
parent 670fffef37
commit 08c1d3660c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 106 additions and 11 deletions

View file

@ -29,10 +29,10 @@ use crate::types::typed_dict::typed_dict_params_from_class_def;
use crate::types::{
ApplyTypeMappingVisitor, Binding, BoundSuperError, BoundSuperType, CallableType,
DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, NormalizedVisitor,
PropertyInstanceType, StringLiteralType, TypeAliasType, TypeMapping, TypeRelation,
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypedDictParams, UnionBuilder,
VarianceInferable, declaration_type, infer_definition_types,
IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, MaterializationKind,
NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeAliasType, TypeMapping,
TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypedDictParams,
UnionBuilder, VarianceInferable, declaration_type, infer_definition_types,
};
use crate::{
Db, FxIndexMap, FxOrderSet, Program,
@ -1470,6 +1470,18 @@ impl<'db> ClassLiteral<'db> {
})
}
pub(crate) fn top_materialization(self, db: &'db dyn Db) -> ClassType<'db> {
self.apply_specialization(db, |generic_context| {
generic_context
.default_specialization(db, self.known(db))
.materialize_impl(
db,
MaterializationKind::Top,
&ApplyTypeMappingVisitor::default(),
)
})
}
/// Returns the default specialization of this class. For non-generic classes, the class is
/// returned unchanged. For a non-specialized generic class, we return a generic alias that
/// applies the default specialization to the class's typevars.

View file

@ -174,10 +174,10 @@ impl ClassInfoConstraintFunction {
fn generate_constraint<'db>(self, db: &'db dyn Db, classinfo: Type<'db>) -> Option<Type<'db>> {
let constraint_fn = |class: ClassLiteral<'db>| match self {
ClassInfoConstraintFunction::IsInstance => {
Type::instance(db, class.default_specialization(db))
Type::instance(db, class.top_materialization(db))
}
ClassInfoConstraintFunction::IsSubclass => {
SubclassOfType::from(db, class.default_specialization(db))
SubclassOfType::from(db, class.top_materialization(db))
}
};