mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Simplify materialization of specialized generics (#20121)
This is a variant of #20076 that moves some complexity out of `apply_type_mapping_impl` in `generics.rs`. The tradeoff is that now every place that applies `TypeMapping::Specialization` must take care to call `.materialize()` afterwards. (A previous version of this didn't work because I had missed a spot where I had to call `.materialize()`.) @carljm as asked in https://github.com/astral-sh/ruff/pull/20076#discussion_r2305385298 .
This commit is contained in:
parent
ca1f66a657
commit
3927b0c931
3 changed files with 33 additions and 55 deletions
|
@ -5970,7 +5970,12 @@ impl<'db> Type<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
specialization: Specialization<'db>,
|
specialization: Specialization<'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
self.apply_type_mapping(db, &TypeMapping::Specialization(specialization))
|
let new_specialization =
|
||||||
|
self.apply_type_mapping(db, &TypeMapping::Specialization(specialization));
|
||||||
|
match specialization.materialization_kind(db) {
|
||||||
|
None => new_specialization,
|
||||||
|
Some(materialization_kind) => new_specialization.materialize(db, materialization_kind),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(
|
fn apply_type_mapping<'a>(
|
||||||
|
@ -6713,13 +6718,6 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materialization_kind(&self, db: &'db dyn Db) -> Option<MaterializationKind> {
|
|
||||||
match self {
|
|
||||||
TypeMapping::Specialization(specialization) => specialization.materialization_kind(db),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Singleton types that are heavily special-cased by ty. Despite its name,
|
/// Singleton types that are heavily special-cased by ty. Despite its name,
|
||||||
|
|
|
@ -4,7 +4,8 @@ use crate::types::generics::Specialization;
|
||||||
use crate::types::tuple::TupleType;
|
use crate::types::tuple::TupleType;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, ClassLiteral, ClassType, DynamicType, KnownClass, KnownInstanceType,
|
ApplyTypeMappingVisitor, ClassLiteral, ClassType, DynamicType, KnownClass, KnownInstanceType,
|
||||||
MroError, MroIterator, NormalizedVisitor, SpecialFormType, Type, TypeMapping, todo_type,
|
MaterializationKind, MroError, MroIterator, NormalizedVisitor, SpecialFormType, Type,
|
||||||
|
TypeMapping, todo_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Enumeration of the possible kinds of types we allow in class bases.
|
/// Enumeration of the possible kinds of types we allow in class bases.
|
||||||
|
@ -286,16 +287,30 @@ impl<'db> ClassBase<'db> {
|
||||||
specialization: Option<Specialization<'db>>,
|
specialization: Option<Specialization<'db>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if let Some(specialization) = specialization {
|
if let Some(specialization) = specialization {
|
||||||
self.apply_type_mapping_impl(
|
let new_self = self.apply_type_mapping_impl(
|
||||||
db,
|
db,
|
||||||
&TypeMapping::Specialization(specialization),
|
&TypeMapping::Specialization(specialization),
|
||||||
&ApplyTypeMappingVisitor::default(),
|
&ApplyTypeMappingVisitor::default(),
|
||||||
)
|
);
|
||||||
|
match specialization.materialization_kind(db) {
|
||||||
|
None => new_self,
|
||||||
|
Some(materialization_kind) => new_self.materialize(db, materialization_kind),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn materialize(self, db: &'db dyn Db, kind: MaterializationKind) -> Self {
|
||||||
|
match self {
|
||||||
|
ClassBase::Class(class) => Self::Class(class.materialize(db, kind)),
|
||||||
|
ClassBase::Dynamic(_)
|
||||||
|
| ClassBase::Generic
|
||||||
|
| ClassBase::Protocol
|
||||||
|
| ClassBase::TypedDict => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn has_cyclic_mro(self, db: &'db dyn Db) -> bool {
|
pub(super) fn has_cyclic_mro(self, db: &'db dyn Db) -> bool {
|
||||||
match self {
|
match self {
|
||||||
ClassBase::Class(class) => {
|
ClassBase::Class(class) => {
|
||||||
|
|
|
@ -581,7 +581,11 @@ impl<'db> Specialization<'db> {
|
||||||
/// That lets us produce the generic alias `A[int]`, which is the corresponding entry in the
|
/// That lets us produce the generic alias `A[int]`, which is the corresponding entry in the
|
||||||
/// MRO of `B[int]`.
|
/// MRO of `B[int]`.
|
||||||
pub(crate) fn apply_specialization(self, db: &'db dyn Db, other: Specialization<'db>) -> Self {
|
pub(crate) fn apply_specialization(self, db: &'db dyn Db, other: Specialization<'db>) -> Self {
|
||||||
self.apply_type_mapping(db, &TypeMapping::Specialization(other))
|
let new_specialization = self.apply_type_mapping(db, &TypeMapping::Specialization(other));
|
||||||
|
match other.materialization_kind(db) {
|
||||||
|
None => new_specialization,
|
||||||
|
Some(materialization_kind) => new_specialization.materialize(db, materialization_kind),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_type_mapping<'a>(
|
pub(crate) fn apply_type_mapping<'a>(
|
||||||
|
@ -598,59 +602,20 @@ impl<'db> Specialization<'db> {
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
visitor: &ApplyTypeMappingVisitor<'db>,
|
visitor: &ApplyTypeMappingVisitor<'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// TODO it seems like this should be possible to do in a much simpler way in
|
|
||||||
// `Self::apply_specialization`; just apply the type mapping to create the new
|
|
||||||
// specialization, then materialize the new specialization appropriately, if the type
|
|
||||||
// mapping is a materialization. But this doesn't work; see discussion in
|
|
||||||
// https://github.com/astral-sh/ruff/pull/20076
|
|
||||||
let applied_materialization_kind = type_mapping.materialization_kind(db);
|
|
||||||
let mut has_dynamic_invariant_typevar = false;
|
|
||||||
let types: Box<[_]> = self
|
let types: Box<[_]> = self
|
||||||
.generic_context(db)
|
.types(db)
|
||||||
.variables(db)
|
.iter()
|
||||||
.into_iter()
|
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor))
|
||||||
.zip(self.types(db))
|
|
||||||
.map(|(bound_typevar, vartype)| {
|
|
||||||
let ty = vartype.apply_type_mapping_impl(db, type_mapping, visitor);
|
|
||||||
match (applied_materialization_kind, bound_typevar.variance(db)) {
|
|
||||||
(None, _) => ty,
|
|
||||||
(Some(_), TypeVarVariance::Bivariant) =>
|
|
||||||
// With bivariance, all specializations are subtypes of each other,
|
|
||||||
// so any materialization is acceptable.
|
|
||||||
{
|
|
||||||
ty.materialize(db, MaterializationKind::Top)
|
|
||||||
}
|
|
||||||
(Some(materialization_kind), TypeVarVariance::Covariant) => {
|
|
||||||
ty.materialize(db, materialization_kind)
|
|
||||||
}
|
|
||||||
(Some(materialization_kind), TypeVarVariance::Contravariant) => {
|
|
||||||
ty.materialize(db, materialization_kind.flip())
|
|
||||||
}
|
|
||||||
(Some(_), TypeVarVariance::Invariant) => {
|
|
||||||
let top_materialization = ty.materialize(db, MaterializationKind::Top);
|
|
||||||
if !ty.is_equivalent_to(db, top_materialization) {
|
|
||||||
has_dynamic_invariant_typevar = true;
|
|
||||||
}
|
|
||||||
ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tuple_inner = self
|
let tuple_inner = self
|
||||||
.tuple_inner(db)
|
.tuple_inner(db)
|
||||||
.and_then(|tuple| tuple.apply_type_mapping_impl(db, type_mapping, visitor));
|
.and_then(|tuple| tuple.apply_type_mapping_impl(db, type_mapping, visitor));
|
||||||
let new_materialization_kind = if has_dynamic_invariant_typevar {
|
|
||||||
self.materialization_kind(db)
|
|
||||||
.or(applied_materialization_kind)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
Specialization::new(
|
Specialization::new(
|
||||||
db,
|
db,
|
||||||
self.generic_context(db),
|
self.generic_context(db),
|
||||||
types,
|
types,
|
||||||
new_materialization_kind,
|
self.materialization_kind(db),
|
||||||
tuple_inner,
|
tuple_inner,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue