New KnownInstanceType

This commit is contained in:
David Peter 2025-11-26 13:24:21 +01:00
parent 54c88b599d
commit 96e7ebb588
8 changed files with 163 additions and 169 deletions

View File

@ -906,12 +906,12 @@ impl<'db> Type<'db> {
matches!(self, Type::GenericAlias(_)) matches!(self, Type::GenericAlias(_))
} }
pub(crate) fn as_generic_alias(&self) -> Option<GenericAlias<'db>> { // pub(crate) fn as_generic_alias(&self) -> Option<GenericAlias<'db>> {
match self { // match self {
Type::GenericAlias(alias) => Some(*alias), // Type::GenericAlias(alias) => Some(*alias),
_ => None, // _ => None,
} // }
} // }
const fn is_dynamic(&self) -> bool { const fn is_dynamic(&self) -> bool {
matches!(self, Type::Dynamic(_)) matches!(self, Type::Dynamic(_))
@ -6599,7 +6599,7 @@ impl<'db> Type<'db> {
.map(|specialization| { .map(|specialization| {
Type::instance( Type::instance(
db, db,
generic_origin.apply_specialization(db, |_| specialization, None), generic_origin.apply_specialization(db, |_| specialization),
) )
}) })
.unwrap_or(instance_ty); .unwrap_or(instance_ty);
@ -6824,6 +6824,7 @@ impl<'db> Type<'db> {
Ok(ty.to_meta_type(db)) Ok(ty.to_meta_type(db))
} }
} }
KnownInstanceType::GenericAlias(instance) => Ok(instance.inner(db)),
KnownInstanceType::Callable(instance) => { KnownInstanceType::Callable(instance) => {
Ok(Type::Callable(instance.callable_type(db))) Ok(Type::Callable(instance.callable_type(db)))
} }
@ -7111,11 +7112,7 @@ impl<'db> Type<'db> {
pub(crate) fn dunder_class(self, db: &'db dyn Db) -> Type<'db> { pub(crate) fn dunder_class(self, db: &'db dyn Db) -> Type<'db> {
if self.is_typed_dict() { if self.is_typed_dict() {
return KnownClass::Dict return KnownClass::Dict
.to_specialized_class_type( .to_specialized_class_type(db, [KnownClass::Str.to_instance(db), Type::object()])
db,
[KnownClass::Str.to_instance(db), Type::object()],
None,
)
.map(Type::from) .map(Type::from)
// Guard against user-customized typesheds with a broken `dict` class // Guard against user-customized typesheds with a broken `dict` class
.unwrap_or_else(Type::unknown); .unwrap_or_else(Type::unknown);
@ -7272,6 +7269,15 @@ impl<'db> Type<'db> {
) )
)) ))
}, },
KnownInstanceType::GenericAlias(instance) => {
Type::KnownInstance(KnownInstanceType::GenericAlias(
TypeInContext::new(
db,
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
instance.binding_context(db),
)
))
},
KnownInstanceType::Callable(instance) => { KnownInstanceType::Callable(instance) => {
Type::KnownInstance(KnownInstanceType::Callable(CallableTypeInstance::new( Type::KnownInstance(KnownInstanceType::Callable(CallableTypeInstance::new(
db, db,
@ -7617,6 +7623,10 @@ impl<'db> Type<'db> {
ty.inner(db) ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor); .find_legacy_typevars_impl(db, binding_context, typevars, visitor);
} }
KnownInstanceType::GenericAlias(ty) => {
ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::SubscriptedProtocol(_) KnownInstanceType::SubscriptedProtocol(_)
| KnownInstanceType::SubscriptedGeneric(_) | KnownInstanceType::SubscriptedGeneric(_)
| KnownInstanceType::TypeVar(_) | KnownInstanceType::TypeVar(_)
@ -8205,6 +8215,8 @@ pub enum KnownInstanceType<'db> {
/// An instance of `typing.GenericAlias` representing a `type[...]` expression. /// An instance of `typing.GenericAlias` representing a `type[...]` expression.
TypeGenericAlias(TypeInContext<'db>), TypeGenericAlias(TypeInContext<'db>),
GenericAlias(TypeInContext<'db>),
/// An instance of `typing.GenericAlias` representing a `Callable[...]` expression. /// An instance of `typing.GenericAlias` representing a `Callable[...]` expression.
Callable(CallableTypeInstance<'db>), Callable(CallableTypeInstance<'db>),
@ -8248,7 +8260,9 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
visitor.visit_type(db, *union_type); visitor.visit_type(db, *union_type);
} }
} }
KnownInstanceType::Annotated(instance) | KnownInstanceType::TypeGenericAlias(instance) => { KnownInstanceType::Annotated(instance)
| KnownInstanceType::TypeGenericAlias(instance)
| KnownInstanceType::GenericAlias(instance) => {
visitor.visit_type(db, instance.inner(db)); visitor.visit_type(db, instance.inner(db));
} }
KnownInstanceType::Literal(ty) | KnownInstanceType::LiteralStringAlias(ty) => { KnownInstanceType::Literal(ty) | KnownInstanceType::LiteralStringAlias(ty) => {
@ -8294,6 +8308,7 @@ impl<'db> KnownInstanceType<'db> {
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)), Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)), Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
Self::TypeGenericAlias(ty) => Self::TypeGenericAlias(ty.normalized_impl(db, visitor)), Self::TypeGenericAlias(ty) => Self::TypeGenericAlias(ty.normalized_impl(db, visitor)),
Self::GenericAlias(ty) => Self::GenericAlias(ty.normalized_impl(db, visitor)),
Self::Callable(callable) => Self::Callable(callable.normalized_impl(db, visitor)), Self::Callable(callable) => Self::Callable(callable.normalized_impl(db, visitor)),
Self::LiteralStringAlias(ty) => { Self::LiteralStringAlias(ty) => {
Self::LiteralStringAlias(ty.normalized_impl(db, visitor)) Self::LiteralStringAlias(ty.normalized_impl(db, visitor))
@ -8332,6 +8347,7 @@ impl<'db> KnownInstanceType<'db> {
Self::Literal(_) Self::Literal(_)
| Self::Annotated(_) | Self::Annotated(_)
| Self::TypeGenericAlias(_) | Self::TypeGenericAlias(_)
| Self::GenericAlias(_)
| Self::Callable(_) => KnownClass::GenericAlias, | Self::Callable(_) => KnownClass::GenericAlias,
Self::LiteralStringAlias(_) => KnownClass::Str, Self::LiteralStringAlias(_) => KnownClass::Str,
Self::NewType(_) => KnownClass::NewType, Self::NewType(_) => KnownClass::NewType,
@ -8437,6 +8453,7 @@ impl<'db> KnownInstanceType<'db> {
KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => { KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => {
f.write_str("GenericAlias") f.write_str("GenericAlias")
} }
KnownInstanceType::GenericAlias(_) => f.write_str("GenericAlias(…)"), //TODO
KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"), KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"),
KnownInstanceType::NewType(declaration) => { KnownInstanceType::NewType(declaration) => {
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db)) write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))

View File

@ -103,7 +103,7 @@ fn try_mro_cycle_initial<'db>(
) -> Result<Mro<'db>, MroError<'db>> { ) -> Result<Mro<'db>, MroError<'db>> {
Err(MroError::cycle( Err(MroError::cycle(
db, db,
self_.apply_optional_specialization(db, specialization, None), self_.apply_optional_specialization(db, specialization),
)) ))
} }
@ -233,8 +233,6 @@ impl<'db> CodeGeneratorKind<'db> {
pub struct GenericAlias<'db> { pub struct GenericAlias<'db> {
pub(crate) origin: ClassLiteral<'db>, pub(crate) origin: ClassLiteral<'db>,
pub(crate) specialization: Specialization<'db>, pub(crate) specialization: Specialization<'db>,
pub(crate) binding_context: Option<Definition<'db>>,
} }
pub(super) fn walk_generic_alias<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>( pub(super) fn walk_generic_alias<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
@ -254,7 +252,6 @@ impl<'db> GenericAlias<'db> {
db, db,
self.origin(db), self.origin(db),
self.specialization(db).normalized_impl(db, visitor), self.specialization(db).normalized_impl(db, visitor),
self.binding_context(db),
) )
} }
@ -280,7 +277,6 @@ impl<'db> GenericAlias<'db> {
self.origin(db), self.origin(db),
self.specialization(db) self.specialization(db)
.apply_type_mapping_impl(db, type_mapping, tcx, visitor), .apply_type_mapping_impl(db, type_mapping, tcx, visitor),
self.binding_context(db),
) )
} }
@ -1533,7 +1529,6 @@ impl<'db> ClassLiteral<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>, f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>,
binding_context: Option<Definition<'db>>,
) -> ClassType<'db> { ) -> ClassType<'db> {
match self.generic_context(db) { match self.generic_context(db) {
None => ClassType::NonGeneric(self), None => ClassType::NonGeneric(self),
@ -1550,7 +1545,7 @@ impl<'db> ClassLiteral<'db> {
} }
} }
ClassType::Generic(GenericAlias::new(db, self, specialization, binding_context)) ClassType::Generic(GenericAlias::new(db, self, specialization))
} }
} }
} }
@ -1559,63 +1554,48 @@ impl<'db> ClassLiteral<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
specialization: Option<Specialization<'db>>, specialization: Option<Specialization<'db>>,
binding_context: Option<Definition<'db>>,
) -> ClassType<'db> { ) -> ClassType<'db> {
self.apply_specialization( self.apply_specialization(db, |generic_context| {
db, specialization
|generic_context| { .unwrap_or_else(|| generic_context.default_specialization(db, self.known(db)))
specialization })
.unwrap_or_else(|| generic_context.default_specialization(db, self.known(db)))
},
binding_context,
)
} }
pub(crate) fn top_materialization(self, db: &'db dyn Db) -> ClassType<'db> { pub(crate) fn top_materialization(self, db: &'db dyn Db) -> ClassType<'db> {
self.apply_specialization( self.apply_specialization(db, |generic_context| {
db, generic_context
|generic_context| { .default_specialization(db, self.known(db))
generic_context .materialize_impl(
.default_specialization(db, self.known(db)) db,
.materialize_impl( MaterializationKind::Top,
db, &ApplyTypeMappingVisitor::default(),
MaterializationKind::Top, )
&ApplyTypeMappingVisitor::default(), })
)
},
None,
)
} }
/// Returns the default specialization of this class. For non-generic classes, the class is /// 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 /// returned unchanged. For a non-specialized generic class, we return a generic alias that
/// applies the default specialization to the class's typevars. /// applies the default specialization to the class's typevars.
pub(crate) fn default_specialization(self, db: &'db dyn Db) -> ClassType<'db> { pub(crate) fn default_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
self.apply_specialization( self.apply_specialization(db, |generic_context| {
db, generic_context.default_specialization(db, self.known(db))
|generic_context| generic_context.default_specialization(db, self.known(db)), })
None,
)
} }
/// Returns the unknown specialization of this class. For non-generic classes, the class is /// Returns the unknown 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 /// returned unchanged. For a non-specialized generic class, we return a generic alias that
/// maps each of the class's typevars to `Unknown`. /// maps each of the class's typevars to `Unknown`.
pub(crate) fn unknown_specialization(self, db: &'db dyn Db) -> ClassType<'db> { pub(crate) fn unknown_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
self.apply_specialization( self.apply_specialization(db, |generic_context| {
db, generic_context.unknown_specialization(db)
|generic_context| generic_context.unknown_specialization(db), })
None,
)
} }
/// Returns a specialization of this class where each typevar is mapped to itself. /// Returns a specialization of this class where each typevar is mapped to itself.
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> ClassType<'db> { pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
self.apply_specialization( self.apply_specialization(db, |generic_context| {
db, generic_context.identity_specialization(db)
|generic_context| generic_context.identity_specialization(db), })
None,
)
} }
/// Return an iterator over the inferred types of this class's *explicit* bases. /// Return an iterator over the inferred types of this class's *explicit* bases.
@ -1646,7 +1626,7 @@ impl<'db> ClassLiteral<'db> {
Box::new([ Box::new([
definition_expression_type(db, class_definition, &class_stmt.bases()[0]), definition_expression_type(db, class_definition, &class_stmt.bases()[0]),
Type::from(tuple_type.to_class_type(db, None)), Type::from(tuple_type.to_class_type(db)),
]) ])
} else { } else {
class_stmt class_stmt
@ -2291,10 +2271,8 @@ impl<'db> ClassLiteral<'db> {
|| transformer_params.is_some_and(|params| params.flags(db).contains(param)) || transformer_params.is_some_and(|params| params.flags(db).contains(param))
}; };
let instance_ty = Type::instance( let instance_ty =
db, Type::instance(db, self.apply_optional_specialization(db, specialization));
self.apply_optional_specialization(db, specialization, None),
);
let signature_from_fields = |mut parameters: Vec<_>, return_ty: Option<Type<'db>>| { let signature_from_fields = |mut parameters: Vec<_>, return_ty: Option<Type<'db>>| {
for (field_name, field) in self.fields(db, specialization, field_policy) { for (field_name, field) in self.fields(db, specialization, field_policy) {
@ -4809,7 +4787,6 @@ impl KnownClass {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
specialization: impl IntoIterator<Item = Type<'db>>, specialization: impl IntoIterator<Item = Type<'db>>,
binding_context: Option<Definition<'db>>,
) -> Option<ClassType<'db>> { ) -> Option<ClassType<'db>> {
let Type::ClassLiteral(class_literal) = self.to_class_literal(db) else { let Type::ClassLiteral(class_literal) = self.to_class_literal(db) else {
return None; return None;
@ -4831,11 +4808,7 @@ impl KnownClass {
return Some(class_literal.default_specialization(db)); return Some(class_literal.default_specialization(db));
} }
Some(class_literal.apply_specialization( Some(class_literal.apply_specialization(db, |_| generic_context.specialize(db, types)))
db,
|_| generic_context.specialize(db, types),
binding_context,
))
} }
/// Lookup a [`KnownClass`] in typeshed and return a [`Type`] /// Lookup a [`KnownClass`] in typeshed and return a [`Type`]
@ -4854,7 +4827,7 @@ impl KnownClass {
KnownClass::Tuple, KnownClass::Tuple,
"Use `Type::heterogeneous_tuple` or `Type::homogeneous_tuple` to create `tuple` instances" "Use `Type::heterogeneous_tuple` or `Type::homogeneous_tuple` to create `tuple` instances"
); );
self.to_specialized_class_type(db, specialization, None) self.to_specialized_class_type(db, specialization)
.and_then(|class_type| Type::from(class_type).to_instance(db)) .and_then(|class_type| Type::from(class_type).to_instance(db))
.unwrap_or_else(Type::unknown) .unwrap_or_else(Type::unknown)
} }

View File

@ -185,6 +185,9 @@ impl<'db> ClassBase<'db> {
KnownInstanceType::TypeGenericAlias(_) => { KnownInstanceType::TypeGenericAlias(_) => {
Self::try_from_type(db, KnownClass::Type.to_class_literal(db), subclass) Self::try_from_type(db, KnownClass::Type.to_class_literal(db), subclass)
} }
KnownInstanceType::GenericAlias(instance) => {
Self::try_from_type(db, instance.inner(db), subclass)
}
KnownInstanceType::Annotated(ty) => { KnownInstanceType::Annotated(ty) => {
// Unions are not supported in this position, so we only need to support // Unions are not supported in this position, so we only need to support
// something like `class C(Annotated[Base, "metadata"]): ...`, which we // something like `class C(Annotated[Base, "metadata"]): ...`, which we
@ -239,7 +242,7 @@ impl<'db> ClassBase<'db> {
db, db,
fields.values().map(|field| field.declared_ty), fields.values().map(|field| field.declared_ty),
)? )?
.to_class_type(db, None) .to_class_type(db)
.into(), .into(),
subclass, subclass,
) )

View File

@ -7500,7 +7500,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} }
let class_type = let class_type =
class_literal.apply_specialization(self.db(), |_| builder.build(generic_context), None); class_literal.apply_specialization(self.db(), |_| builder.build(generic_context));
Type::from(class_type).to_instance(self.db()) Type::from(class_type).to_instance(self.db())
} }
@ -10813,18 +10813,48 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// If we have an implicit type alias like `MyList = list[T]`, and if `MyList` is being // If we have an implicit type alias like `MyList = list[T]`, and if `MyList` is being
// used in another implicit type alias like `Numbers = MyList[int]`, then we infer the // used in another implicit type alias like `Numbers = MyList[int]`, then we infer the
// right hand side as a value expression, and need to handle the specialization here. // right hand side as a value expression, and need to handle the specialization here.
if let Some(alias) = value_ty.as_generic_alias() { if let Type::KnownInstance(KnownInstanceType::GenericAlias(alias)) = value_ty {
let return_ty = self.infer_explicitly_specialized_type_alias( return Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
subscript, self.db(),
value_ty, self.infer_explicitly_specialized_type_alias(
alias.binding_context(self.db()), subscript,
false, value_ty,
); alias.binding_context(self.db()),
false,
return return_ty; ),
self.typevar_binding_context,
)));
} }
self.infer_subscript_load_impl(value_ty, subscript) // if let Type::GenericAlias(alias) = value_ty {
// return Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
// self.db(),
// self.infer_explicitly_specialized_type_alias(
// subscript,
// value_ty,
// Some(alias.definition(self.db())),
// false,
// ),
// self.typevar_binding_context,
// )));
// }
let result_ty = self.infer_subscript_load_impl(value_ty, subscript);
// let result_ty = if result_ty.is_generic_alias() {
// Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
// self.db(),
// result_ty,
// self.typevar_binding_context,
// )))
// } else {
// result_ty
// };
// eprintln!("Subscripting type: {}", value_ty.display(self.db()));
// eprintln!("Inferred subscript type: {}", result_ty.display(self.db()));
result_ty
} }
fn infer_subscript_load_impl( fn infer_subscript_load_impl(
@ -10860,11 +10890,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} }
} }
let db = self.db(); let tuple_generic_alias = |db: &'db dyn Db, tuple: Option<TupleType<'db>>| {
let typevar_binding_context = self.typevar_binding_context;
let tuple_generic_alias = |tuple: Option<TupleType<'db>>| {
let tuple = tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown())); let tuple = tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown()));
Type::from(tuple.to_class_type(db, typevar_binding_context)) Type::from(tuple.to_class_type(db))
}; };
match value_ty { match value_ty {
@ -10876,7 +10904,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// updating all of the subscript logic below to use custom callables for all of the _other_ // updating all of the subscript logic below to use custom callables for all of the _other_
// special cases, too. // special cases, too.
if class.is_tuple(self.db()) { if class.is_tuple(self.db()) {
return tuple_generic_alias(self.infer_tuple_type_expression(slice)); return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
} else if class.is_known(self.db(), KnownClass::Type) { } else if class.is_known(self.db(), KnownClass::Type) {
let argument_ty = self.infer_type_expression(slice); let argument_ty = self.infer_type_expression(slice);
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias( return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
@ -10904,7 +10932,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} }
} }
Type::SpecialForm(SpecialFormType::Tuple) => { Type::SpecialForm(SpecialFormType::Tuple) => {
return tuple_generic_alias(self.infer_tuple_type_expression(slice)); return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
} }
Type::SpecialForm(SpecialFormType::Literal) => { Type::SpecialForm(SpecialFormType::Literal) => {
match self.infer_literal_parameter_type(slice) { match self.infer_literal_parameter_type(slice) {
@ -11070,11 +11098,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.expect("A known stdlib class is available"); .expect("A known stdlib class is available");
return class return class
.to_specialized_class_type( .to_specialized_class_type(self.db(), [element_ty])
self.db(),
[element_ty],
self.typevar_binding_context,
)
.map(Type::from) .map(Type::from)
.unwrap_or_else(Type::unknown); .unwrap_or_else(Type::unknown);
} }
@ -11132,11 +11156,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
.expect("Stdlib class available"); .expect("Stdlib class available");
return class return class
.to_specialized_class_type( .to_specialized_class_type(self.db(), [first_ty, second_ty])
self.db(),
[first_ty, second_ty],
self.typevar_binding_context,
)
.map(Type::from) .map(Type::from)
.unwrap_or_else(Type::unknown); .unwrap_or_else(Type::unknown);
} }
@ -11183,13 +11203,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
generic_context: GenericContext<'db>, generic_context: GenericContext<'db>,
) -> Type<'db> { ) -> Type<'db> {
let db = self.db(); let db = self.db();
let typevar_binding_context = self.typevar_binding_context;
let specialize = |types: &[Option<Type<'db>>]| { let specialize = |types: &[Option<Type<'db>>]| {
Type::from(generic_class.apply_specialization( Type::from(generic_class.apply_specialization(db, |_| {
db, generic_context.specialize_partial(db, types.iter().copied())
|_| generic_context.specialize_partial(db, types.iter().copied()), }))
typevar_binding_context,
))
}; };
self.infer_explicit_callable_specialization( self.infer_explicit_callable_specialization(

View File

@ -13,10 +13,10 @@ use crate::types::string_annotation::parse_string_annotation;
use crate::types::tuple::{TupleSpecBuilder, TupleType}; use crate::types::tuple::{TupleSpecBuilder, TupleType};
use crate::types::visitor::any_over_type; use crate::types::visitor::any_over_type;
use crate::types::{ use crate::types::{
BindingContext, CallableType, DynamicType, GenericAlias, GenericContext, IntersectionBuilder, BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
KnownClass, KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
SubclassOfType, Type, TypeAliasType, TypeContext, TypeInContext, TypeIsType, TypeMapping, Type, TypeAliasType, TypeContext, TypeInContext, TypeIsType, TypeMapping, UnionBuilder,
UnionBuilder, UnionType, todo_type, UnionType, todo_type,
}; };
/// Type expressions /// Type expressions
@ -713,20 +713,13 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
match class_literal.generic_context(self.db()) { match class_literal.generic_context(self.db()) {
Some(generic_context) => { Some(generic_context) => {
let db = self.db(); let db = self.db();
let typevar_binding_context = self.typevar_binding_context;
let specialize = |types: &[Option<Type<'db>>]| { let specialize = |types: &[Option<Type<'db>>]| {
SubclassOfType::from( SubclassOfType::from(
db, db,
class_literal.apply_specialization( class_literal.apply_specialization(db, |_| {
db, generic_context
|_| { .specialize_partial(db, types.iter().copied())
generic_context.specialize_partial( }),
db,
types.iter().copied(),
)
},
typevar_binding_context,
),
) )
}; };
self.infer_explicit_callable_specialization( self.infer_explicit_callable_specialization(
@ -810,12 +803,12 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
} else { } else {
// Update the binding context // Update the binding context
match specialized { match specialized {
Type::GenericAlias(alias) => Type::GenericAlias(GenericAlias::new( // Type::GenericAlias(alias) => Type::GenericAlias(GenericAlias::new(
db, // db,
alias.origin(db), // alias.origin(db),
alias.specialization(db), // alias.specialization(db),
current_typevar_binding_context, // current_typevar_binding_context,
)), // )),
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(instance)) => { Type::KnownInstance(KnownInstanceType::TypeGenericAlias(instance)) => {
Type::KnownInstance(KnownInstanceType::TypeGenericAlias( Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
TypeInContext::new( TypeInContext::new(
@ -995,7 +988,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
true, true,
), ),
KnownInstanceType::Annotated(instance) KnownInstanceType::Annotated(instance)
| KnownInstanceType::TypeGenericAlias(instance) => self | KnownInstanceType::TypeGenericAlias(instance)
| KnownInstanceType::GenericAlias(instance) => self
.infer_explicitly_specialized_type_alias( .infer_explicitly_specialized_type_alias(
subscript, subscript,
value_ty, value_ty,
@ -1049,12 +1043,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
} }
} }
} }
Type::GenericAlias(alias) => self.infer_explicitly_specialized_type_alias( // Type::GenericAlias(alias) => {
subscript, // self.infer_explicitly_specialized_type_alias(subscript, value_ty, None, true)
value_ty, // }
alias.binding_context(self.db()),
true,
),
Type::StringLiteral(_) => { Type::StringLiteral(_) => {
self.infer_type_expression(slice); self.infer_type_expression(slice);
// For stringified TypeAlias; remove once properly supported // For stringified TypeAlias; remove once properly supported
@ -1063,6 +1054,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
_ => { _ => {
self.infer_type_expression(slice); self.infer_type_expression(slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
dbg!(&value_ty);
builder.into_diagnostic(format_args!( builder.into_diagnostic(format_args!(
"Invalid subscript of object of type `{}` in type expression", "Invalid subscript of object of type `{}` in type expression",
value_ty.display(self.db()) value_ty.display(self.db())

View File

@ -213,7 +213,7 @@ pub(super) fn walk_nominal_instance_type<'db, V: super::visitor::TypeVisitor<'db
impl<'db> NominalInstanceType<'db> { impl<'db> NominalInstanceType<'db> {
pub(super) fn class(&self, db: &'db dyn Db) -> ClassType<'db> { pub(super) fn class(&self, db: &'db dyn Db) -> ClassType<'db> {
match self.0 { match self.0 {
NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db, None), NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db),
NominalInstanceInner::NonTuple(class) => class, NominalInstanceInner::NonTuple(class) => class,
NominalInstanceInner::Object => KnownClass::Object NominalInstanceInner::Object => KnownClass::Object
.try_to_class_literal(db) .try_to_class_literal(db)
@ -224,7 +224,7 @@ impl<'db> NominalInstanceType<'db> {
pub(super) fn class_literal(&self, db: &'db dyn Db) -> ClassLiteral<'db> { pub(super) fn class_literal(&self, db: &'db dyn Db) -> ClassLiteral<'db> {
let class = match self.0 { let class = match self.0 {
NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db, None), NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db),
NominalInstanceInner::NonTuple(class) => class, NominalInstanceInner::NonTuple(class) => class,
NominalInstanceInner::Object => { NominalInstanceInner::Object => {
return KnownClass::Object return KnownClass::Object

View File

@ -51,7 +51,7 @@ impl<'db> Mro<'db> {
class_literal: ClassLiteral<'db>, class_literal: ClassLiteral<'db>,
specialization: Option<Specialization<'db>>, specialization: Option<Specialization<'db>>,
) -> Result<Self, MroError<'db>> { ) -> Result<Self, MroError<'db>> {
let class = class_literal.apply_optional_specialization(db, specialization, None); let class = class_literal.apply_optional_specialization(db, specialization);
// Special-case `NotImplementedType`: typeshed says that it inherits from `Any`, // Special-case `NotImplementedType`: typeshed says that it inherits from `Any`,
// but this causes more problems than it fixes. // but this causes more problems than it fixes.
if class_literal.is_known(db, KnownClass::NotImplementedType) { if class_literal.is_known(db, KnownClass::NotImplementedType) {
@ -100,7 +100,13 @@ impl<'db> Mro<'db> {
if original_bases.contains(&Type::SpecialForm(SpecialFormType::Protocol)) { if original_bases.contains(&Type::SpecialForm(SpecialFormType::Protocol)) {
return; return;
} }
if remaining_bases.iter().any(Type::is_generic_alias) { if remaining_bases.iter().any(|ty| {
matches!(
ty,
Type::GenericAlias(..)
| Type::KnownInstance(KnownInstanceType::GenericAlias(_))
)
}) {
return; return;
} }
resolved_bases.push(ClassBase::Generic); resolved_bases.push(ClassBase::Generic);
@ -412,11 +418,10 @@ impl<'db> Iterator for MroIterator<'db> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if !self.first_element_yielded { if !self.first_element_yielded {
self.first_element_yielded = true; self.first_element_yielded = true;
return Some(ClassBase::Class(self.class.apply_optional_specialization( return Some(ClassBase::Class(
self.db, self.class
self.specialization, .apply_optional_specialization(self.db, self.specialization),
None, ));
)));
} }
self.full_mro_except_first_element().next() self.full_mro_except_first_element().next()
} }

View File

@ -202,27 +202,19 @@ impl<'db> TupleType<'db> {
// `static-frame` as part of a mypy_primer run! This is because it's called // `static-frame` as part of a mypy_primer run! This is because it's called
// from `NominalInstanceType::class()`, which is a very hot method. // from `NominalInstanceType::class()`, which is a very hot method.
#[salsa::tracked(cycle_initial=to_class_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)] #[salsa::tracked(cycle_initial=to_class_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn to_class_type( pub(crate) fn to_class_type(self, db: &'db dyn Db) -> ClassType<'db> {
self,
db: &'db dyn Db,
binding_context: Option<Definition<'db>>,
) -> ClassType<'db> {
let tuple_class = KnownClass::Tuple let tuple_class = KnownClass::Tuple
.try_to_class_literal(db) .try_to_class_literal(db)
.expect("Typeshed should always have a `tuple` class in `builtins.pyi`"); .expect("Typeshed should always have a `tuple` class in `builtins.pyi`");
tuple_class.apply_specialization( tuple_class.apply_specialization(db, |generic_context| {
db, if generic_context.variables(db).len() == 1 {
|generic_context| { let element_type = self.tuple(db).homogeneous_element_type(db);
if generic_context.variables(db).len() == 1 { generic_context.specialize_tuple(db, element_type, self)
let element_type = self.tuple(db).homogeneous_element_type(db); } else {
generic_context.specialize_tuple(db, element_type, self) generic_context.default_specialization(db, Some(KnownClass::Tuple))
} else { }
generic_context.default_specialization(db, Some(KnownClass::Tuple)) })
}
},
binding_context,
)
} }
/// Return a normalized version of `self`. /// Return a normalized version of `self`.
@ -302,23 +294,18 @@ fn to_class_type_cycle_initial<'db>(
db: &'db dyn Db, db: &'db dyn Db,
_id: salsa::Id, _id: salsa::Id,
self_: TupleType<'db>, self_: TupleType<'db>,
binding_context: Option<Definition<'db>>,
) -> ClassType<'db> { ) -> ClassType<'db> {
let tuple_class = KnownClass::Tuple let tuple_class = KnownClass::Tuple
.try_to_class_literal(db) .try_to_class_literal(db)
.expect("Typeshed should always have a `tuple` class in `builtins.pyi`"); .expect("Typeshed should always have a `tuple` class in `builtins.pyi`");
tuple_class.apply_specialization( tuple_class.apply_specialization(db, |generic_context| {
db, if generic_context.variables(db).len() == 1 {
|generic_context| { generic_context.specialize_tuple(db, Type::Never, self_)
if generic_context.variables(db).len() == 1 { } else {
generic_context.specialize_tuple(db, Type::Never, self_) generic_context.default_specialization(db, Some(KnownClass::Tuple))
} else { }
generic_context.default_specialization(db, Some(KnownClass::Tuple)) })
}
},
binding_context,
)
} }
/// A tuple spec describes the contents of a tuple type, which might be fixed- or variable-length. /// A tuple spec describes the contents of a tuple type, which might be fixed- or variable-length.