[ty] Generic implicit types aliases

This commit is contained in:
David Peter
2025-11-21 08:48:32 +01:00
parent dd15656deb
commit 013d43a2dd
5 changed files with 216 additions and 93 deletions

View File

@@ -7198,17 +7198,70 @@ impl<'db> Type<'db> {
}
}
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
TypeMapping::BindLegacyTypevars(binding_context) => {
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
Type::KnownInstance(known_instance) => match known_instance {
KnownInstanceType::TypeVar(typevar) => {
match type_mapping {
TypeMapping::BindLegacyTypevars(binding_context) => {
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
}
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::PromoteLiterals(_) |
TypeMapping::BindSelf(_) |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::Materialize(_) |
TypeMapping::ReplaceParameterDefaults => self,
}
}
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::PromoteLiterals(_) |
TypeMapping::BindSelf(_) |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::Materialize(_) |
TypeMapping::ReplaceParameterDefaults => self,
KnownInstanceType::UnionType(instance) => {
if let Ok(union_type) = instance.union_type(db) {
Type::KnownInstance(KnownInstanceType::UnionType(
UnionTypeInstance::new(
db,
instance._value_expr_types(db),
Ok(union_type.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
)
)))
} else {
self
}
},
KnownInstanceType::Annotated(ty) => {
Type::KnownInstance(KnownInstanceType::Annotated(
InternedType::new(
db,
ty.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)
))
},
KnownInstanceType::Callable(callable_type) => {
Type::KnownInstance(KnownInstanceType::Callable(
callable_type.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
))
},
KnownInstanceType::TypeGenericAlias(ty) => {
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
InternedType::new(
db,
ty.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)
))
},
KnownInstanceType::SubscriptedProtocol(_) |
KnownInstanceType::SubscriptedGeneric(_) |
KnownInstanceType::TypeAliasType(_) |
KnownInstanceType::Deprecated(_) |
KnownInstanceType::Field(_) |
KnownInstanceType::ConstraintSet(_) |
KnownInstanceType::GenericContext(_) |
KnownInstanceType::Specialization(_) |
KnownInstanceType::Literal(_) |
KnownInstanceType::LiteralStringAlias(_) |
KnownInstanceType::NewType(_) => {
// TODO: ?
self
},
}
Type::FunctionLiteral(function) => {
@@ -7369,8 +7422,7 @@ impl<'db> Type<'db> {
// some other generic context's specialization is applied to it.
| Type::ClassLiteral(_)
| Type::BoundSuper(_)
| Type::SpecialForm(_)
| Type::KnownInstance(_) => self,
| Type::SpecialForm(_) => self,
}
}
@@ -7507,6 +7559,44 @@ impl<'db> Type<'db> {
});
}
Type::KnownInstance(known_instance) => match known_instance {
KnownInstanceType::UnionType(instance) => {
if let Ok(union_type) = instance.union_type(db) {
union_type.find_legacy_typevars_impl(
db,
binding_context,
typevars,
visitor,
);
}
}
KnownInstanceType::Annotated(ty) => {
ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::Callable(callable_type) => {
callable_type.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::TypeGenericAlias(ty) => {
ty.inner(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
KnownInstanceType::SubscriptedProtocol(_)
| KnownInstanceType::SubscriptedGeneric(_)
| KnownInstanceType::TypeVar(_)
| KnownInstanceType::TypeAliasType(_)
| KnownInstanceType::Deprecated(_)
| KnownInstanceType::Field(_)
| KnownInstanceType::ConstraintSet(_)
| KnownInstanceType::GenericContext(_)
| KnownInstanceType::Specialization(_)
| KnownInstanceType::Literal(_)
| KnownInstanceType::LiteralStringAlias(_)
| KnownInstanceType::NewType(_) => {
// TODO?
}
},
Type::Dynamic(_)
| Type::Never
| Type::AlwaysTruthy
@@ -7534,7 +7624,6 @@ impl<'db> Type<'db> {
| Type::EnumLiteral(_)
| Type::BoundSuper(_)
| Type::SpecialForm(_)
| Type::KnownInstance(_)
| Type::TypedDict(_) => {}
}
}

View File

@@ -10794,6 +10794,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
fn infer_subscript_load(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
let value_ty = self.infer_expression(&subscript.value, TypeContext::default());
if value_ty.is_generic_alias() {
return self
.infer_explicitly_specialized_implicit_type_alias(subscript, value_ty, false);
}
self.infer_subscript_load_impl(value_ty, subscript)
}

View File

@@ -2,6 +2,7 @@ use itertools::Either;
use ruff_python_ast as ast;
use super::{DeferredExpressionState, TypeInferenceBuilder};
use crate::FxOrderSet;
use crate::types::diagnostic::{
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
report_invalid_arguments_to_callable,
@@ -11,9 +12,9 @@ use crate::types::string_annotation::parse_string_annotation;
use crate::types::tuple::{TupleSpecBuilder, TupleType};
use crate::types::visitor::any_over_type;
use crate::types::{
CallableType, DynamicType, IntersectionBuilder, KnownClass, KnownInstanceType,
LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType, Type,
TypeAliasType, TypeContext, TypeIsType, UnionBuilder, UnionType, todo_type,
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
Type, TypeAliasType, TypeContext, TypeIsType, TypeMapping, UnionBuilder, UnionType, todo_type,
};
/// Type expressions
@@ -750,6 +751,49 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
}
pub(crate) fn infer_explicitly_specialized_implicit_type_alias(
&mut self,
subscript: &ast::ExprSubscript,
value_ty: Type<'db>,
in_type_expression: bool,
) -> Type<'db> {
let db = self.db();
let generic_type_alias = value_ty.apply_type_mapping(
db,
&TypeMapping::BindLegacyTypevars(BindingContext::Synthetic),
TypeContext::default(),
);
let mut variables = FxOrderSet::default();
generic_type_alias.find_legacy_typevars(db, None, &mut variables);
let generic_context = GenericContext::from_typevar_instances(db, variables);
let scope_id = self.scope();
let typevar_binding_context = self.typevar_binding_context;
let specialize = |types: &[Option<Type<'db>>]| {
let specialized = generic_type_alias.apply_specialization(
db,
generic_context.specialize_partial(db, types.iter().copied()),
);
if in_type_expression {
specialized
.in_type_expression(db, scope_id, typevar_binding_context)
.unwrap_or_else(|_| Type::unknown())
} else {
specialized
}
};
self.infer_explicit_callable_specialization(
subscript,
value_ty,
generic_context,
specialize,
)
}
fn infer_subscript_type_expression(
&mut self,
subscript: &ast::ExprSubscript,
@@ -840,10 +884,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
Type::unknown()
}
KnownInstanceType::TypeVar(_) => {
self.infer_type_expression(slice);
todo_type!("TypeVar annotations")
}
KnownInstanceType::TypeAliasType(type_alias @ TypeAliasType::PEP695(_)) => {
match type_alias.generic_context(self.db()) {
Some(generic_context) => {
@@ -886,11 +926,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
self.infer_type_expression(slice);
todo_type!("Generic stringified PEP-613 type alias")
}
KnownInstanceType::UnionType(_) => {
self.infer_type_expression(slice);
todo_type!("Generic specialization of types.UnionType")
}
KnownInstanceType::Literal(ty) | KnownInstanceType::TypeGenericAlias(ty) => {
KnownInstanceType::Literal(ty) => {
self.infer_type_expression(slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
builder.into_diagnostic(format_args!(
@@ -900,13 +936,14 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
Type::unknown()
}
KnownInstanceType::Callable(_) => {
self.infer_type_expression(slice);
todo_type!("Generic specialization of typing.Callable")
}
KnownInstanceType::Annotated(_) => {
self.infer_type_expression(slice);
todo_type!("Generic specialization of typing.Annotated")
KnownInstanceType::TypeVar(_) => self
.infer_explicitly_specialized_implicit_type_alias(subscript, value_ty, false),
KnownInstanceType::UnionType(_)
| KnownInstanceType::Callable(_)
| KnownInstanceType::Annotated(_)
| KnownInstanceType::TypeGenericAlias(_) => {
self.infer_explicitly_specialized_implicit_type_alias(subscript, value_ty, true)
}
KnownInstanceType::NewType(newtype) => {
self.infer_type_expression(&subscript.slice);
@@ -949,11 +986,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
}
Type::GenericAlias(_) => {
self.infer_type_expression(slice);
// If the generic alias is already fully specialized, this is an error. But it
// could have been specialized with another typevar (e.g. a type alias like `MyList
// = list[T]`), in which case it's later valid to do `MyList[int]`.
todo_type!("specialized generic alias in type expression")
self.infer_explicitly_specialized_implicit_type_alias(subscript, value_ty, true)
}
Type::StringLiteral(_) => {
self.infer_type_expression(slice);