improve performance of `find_type_var_from`

This commit is contained in:
Ibraheem Ahmed 2025-12-10 17:53:00 -05:00
parent 19c5cd8018
commit 5d07d58d59
3 changed files with 26 additions and 37 deletions

View File

@ -376,10 +376,13 @@ reveal_type(x6) # revealed: Sub1[Literal[1]]
x7: Sup1[Literal[1]] | None = sub1(1)
reveal_type(x7) # revealed: Sub1[Literal[1]]
class Sup2[T, U]:
class Sup2A[T, U]:
value: tuple[T, U]
class Sub2[T, U](Sup2[T, Any], Sup2[Any, U]): ...
class Sup2B[T, U]:
value: tuple[T, U]
class Sub2[T, U](Sup2A[T, Any], Sup2B[Any, U]): ...
def sub2[T, U](x: T, y: U) -> Sub2[T, U]:
return Sub2()
@ -387,6 +390,9 @@ def sub2[T, U](x: T, y: U) -> Sub2[T, U]:
x8 = sub2(1, 2)
reveal_type(x8) # revealed: Sub2[int, int]
x9: Sup2[Literal[1], Literal[2]] = sub2(1, 2)
reveal_type(x9) # revealed: Sub2[Literal[1], Literal[2]]
x9: Sup2A[Literal[1], Literal[2]] = sub2(1, 2)
reveal_type(x9) # revealed: Sub2[Literal[1], int]
x10: Sup2B[Literal[1], Literal[2]] = sub2(1, 2)
reveal_type(x10) # revealed: Sub2[int, Literal[2]]
```

View File

@ -2,7 +2,6 @@ use compact_str::{CompactString, ToCompactString};
use infer::nearest_enclosing_class;
use itertools::{Either, Itertools};
use ruff_diagnostics::{Edit, Fix};
use rustc_hash::FxHashSet;
use std::borrow::Cow;
use std::time::Duration;
@ -1041,55 +1040,40 @@ impl<'db> Type<'db> {
/// Given a type variable `T` from the generic context of a class `C`:
/// - If `self` is a specialized instance of `C`, returns the type assigned to `T` on `self`.
/// - If `self` is a specialized instance of some class `A`, and `C` is a subclass of `A`
/// such that the type variable `U` on `A` is specialized to `T`, returns the type
/// assigned to `U` on `self`.
/// - If `self` is a specialized instance of some class `A[T]`, and `C[T]` is a subclass of
/// `A[T]`, returns the type assigned to `T` on `self`.
pub(crate) fn find_type_var_from(
self,
db: &'db dyn Db,
bound_typevar: BoundTypeVarInstance<'db>,
class: ClassLiteral<'db>,
) -> Option<Type<'db>> {
self.find_type_var_from_impl(db, bound_typevar, class, &mut FxHashSet::default())
}
pub(crate) fn find_type_var_from_impl(
self,
db: &'db dyn Db,
bound_typevar: BoundTypeVarInstance<'db>,
class: ClassLiteral<'db>,
visited: &mut FxHashSet<(ClassLiteral<'db>, BoundTypeVarIdentity<'db>)>,
) -> Option<Type<'db>> {
if let Some(specialization) = self.specialization_of(db, class) {
return specialization.get(db, bound_typevar);
}
// TODO: We should use the constraint solver here to determine the type mappings for more
// complex subtyping relationships, e.g., `type[C[T]]` to `Callable[..., T]`, or unions
// containing multiple generic elements.
for base in class.iter_mro(db, None) {
let Some((origin, Some(specialization))) =
// complex subtyping relationships, e.g., callables, protocols, or unions containing multiple
// generic elements.
for base in class.iter_mro(db, None).skip(1) {
let Some((base, Some(base_specialization))) =
base.into_class().map(|class| class.class_literal(db))
else {
continue;
};
for (base_typevar, base_ty) in specialization
.generic_context(db)
.variables(db)
.zip(specialization.types(db))
{
if *base_ty == Type::TypeVar(bound_typevar) {
if !visited.insert((origin, base_typevar.identity(db))) {
return None;
}
if let Some(ty) =
self.find_type_var_from_impl(db, base_typevar, origin, visited)
{
return Some(ty);
if let Some(specialization) = self.specialization_of(db, base) {
for (base_typevar, base_ty) in base_specialization
.generic_context(db)
.variables(db)
.zip(base_specialization.types(db))
{
if *base_ty == Type::TypeVar(bound_typevar) {
return specialization.get(db, base_typevar);
}
}
return None;
}
}

View File

@ -8029,7 +8029,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
call_expression: &ast::ExprCall,
tcx: TypeContext<'db>,
) -> Type<'db> {
// TODO: Use the type context for more precise inference.
let callable_type =
self.infer_maybe_standalone_expression(&call_expression.func, TypeContext::default());