mirror of https://github.com/astral-sh/ruff
[WIP] add recursive visitor to more type methods
This commit is contained in:
parent
ef3a195f28
commit
89228c344c
|
|
@ -1258,6 +1258,17 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_relation_to(self, db: &'db dyn Db, target: Type<'db>, relation: TypeRelation) -> bool {
|
fn has_relation_to(self, db: &'db dyn Db, target: Type<'db>, relation: TypeRelation) -> bool {
|
||||||
|
let mut visitor = PairVisitor::new(true);
|
||||||
|
self.has_relation_to_impl(db, target, relation, &mut visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_relation_to_impl(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
target: Type<'db>,
|
||||||
|
relation: TypeRelation,
|
||||||
|
visitor: &mut PairVisitor<'db>,
|
||||||
|
) -> bool {
|
||||||
// Subtyping implies assignability, so if subtyping is reflexive and the two types are
|
// Subtyping implies assignability, so if subtyping is reflexive and the two types are
|
||||||
// equal, it is both a subtype and assignable. Assignability is always reflexive.
|
// equal, it is both a subtype and assignable. Assignability is always reflexive.
|
||||||
//
|
//
|
||||||
|
|
@ -1326,12 +1337,13 @@ impl<'db> Type<'db> {
|
||||||
match typevar.bound_or_constraints(db) {
|
match typevar.bound_or_constraints(db) {
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||||
bound.has_relation_to(db, target, relation)
|
bound.has_relation_to_impl(db, target, relation, visitor)
|
||||||
|
}
|
||||||
|
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||||
|
constraints.elements(db).iter().all(|constraint| {
|
||||||
|
constraint.has_relation_to_impl(db, target, relation, visitor)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
|
||||||
.elements(db)
|
|
||||||
.iter()
|
|
||||||
.all(|constraint| constraint.has_relation_to(db, target, relation)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1340,9 +1352,9 @@ impl<'db> Type<'db> {
|
||||||
// disjoint, which means an lhs type might be a subtype of all of the constraints.
|
// disjoint, which means an lhs type might be a subtype of all of the constraints.
|
||||||
(_, Type::TypeVar(typevar))
|
(_, Type::TypeVar(typevar))
|
||||||
if typevar.constraints(db).is_some_and(|constraints| {
|
if typevar.constraints(db).is_some_and(|constraints| {
|
||||||
constraints
|
constraints.iter().all(|constraint| {
|
||||||
.iter()
|
self.has_relation_to_impl(db, *constraint, relation, visitor)
|
||||||
.all(|constraint| self.has_relation_to(db, *constraint, relation))
|
})
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
true
|
true
|
||||||
|
|
@ -1356,12 +1368,12 @@ impl<'db> Type<'db> {
|
||||||
(Type::Union(union), _) => union
|
(Type::Union(union), _) => union
|
||||||
.elements(db)
|
.elements(db)
|
||||||
.iter()
|
.iter()
|
||||||
.all(|&elem_ty| elem_ty.has_relation_to(db, target, relation)),
|
.all(|&elem_ty| elem_ty.has_relation_to_impl(db, target, relation, visitor)),
|
||||||
|
|
||||||
(_, Type::Union(union)) => union
|
(_, Type::Union(union)) => union
|
||||||
.elements(db)
|
.elements(db)
|
||||||
.iter()
|
.iter()
|
||||||
.any(|&elem_ty| self.has_relation_to(db, elem_ty, relation)),
|
.any(|&elem_ty| self.has_relation_to_impl(db, elem_ty, relation, visitor)),
|
||||||
|
|
||||||
// If both sides are intersections we need to handle the right side first
|
// If both sides are intersections we need to handle the right side first
|
||||||
// (A & B & C) is a subtype of (A & B) because the left is a subtype of both A and B,
|
// (A & B & C) is a subtype of (A & B) because the left is a subtype of both A and B,
|
||||||
|
|
@ -1370,7 +1382,7 @@ impl<'db> Type<'db> {
|
||||||
intersection
|
intersection
|
||||||
.positive(db)
|
.positive(db)
|
||||||
.iter()
|
.iter()
|
||||||
.all(|&pos_ty| self.has_relation_to(db, pos_ty, relation))
|
.all(|&pos_ty| self.has_relation_to_impl(db, pos_ty, relation, visitor))
|
||||||
&& intersection
|
&& intersection
|
||||||
.negative(db)
|
.negative(db)
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -1380,7 +1392,7 @@ impl<'db> Type<'db> {
|
||||||
(Type::Intersection(intersection), _) => intersection
|
(Type::Intersection(intersection), _) => intersection
|
||||||
.positive(db)
|
.positive(db)
|
||||||
.iter()
|
.iter()
|
||||||
.any(|&elem_ty| elem_ty.has_relation_to(db, target, relation)),
|
.any(|&elem_ty| elem_ty.has_relation_to_impl(db, target, relation, visitor)),
|
||||||
|
|
||||||
// Other than the special cases checked above, no other types are a subtype of a
|
// Other than the special cases checked above, no other types are a subtype of a
|
||||||
// typevar, since there's no guarantee what type the typevar will be specialized to.
|
// typevar, since there's no guarantee what type the typevar will be specialized to.
|
||||||
|
|
@ -1438,9 +1450,9 @@ impl<'db> Type<'db> {
|
||||||
self_callable.has_relation_to(db, other_callable, relation)
|
self_callable.has_relation_to(db, other_callable, relation)
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, Type::Callable(_)) => self
|
(_, Type::Callable(_)) => self.into_callable(db).is_some_and(|callable| {
|
||||||
.into_callable(db)
|
callable.has_relation_to_impl(db, target, relation, visitor)
|
||||||
.is_some_and(|callable| callable.has_relation_to(db, target, relation)),
|
}),
|
||||||
|
|
||||||
(Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => {
|
(Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => {
|
||||||
left.has_relation_to(db, right, relation)
|
left.has_relation_to(db, right, relation)
|
||||||
|
|
@ -1476,8 +1488,9 @@ impl<'db> Type<'db> {
|
||||||
| Type::ModuleLiteral(_)
|
| Type::ModuleLiteral(_)
|
||||||
| Type::EnumLiteral(_),
|
| Type::EnumLiteral(_),
|
||||||
_,
|
_,
|
||||||
) => (self.literal_fallback_instance(db))
|
) => (self.literal_fallback_instance(db)).is_some_and(|instance| {
|
||||||
.is_some_and(|instance| instance.has_relation_to(db, target, relation)),
|
instance.has_relation_to_impl(db, target, relation, visitor)
|
||||||
|
}),
|
||||||
|
|
||||||
// A `FunctionLiteral` type is a single-valued type like the other literals handled above,
|
// A `FunctionLiteral` type is a single-valued type like the other literals handled above,
|
||||||
// so it also, for now, just delegates to its instance fallback.
|
// so it also, for now, just delegates to its instance fallback.
|
||||||
|
|
@ -1488,13 +1501,13 @@ impl<'db> Type<'db> {
|
||||||
// The same reasoning applies for these special callable types:
|
// The same reasoning applies for these special callable types:
|
||||||
(Type::BoundMethod(_), _) => KnownClass::MethodType
|
(Type::BoundMethod(_), _) => KnownClass::MethodType
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
(Type::MethodWrapper(_), _) => KnownClass::WrapperDescriptorType
|
(Type::MethodWrapper(_), _) => KnownClass::WrapperDescriptorType
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
(Type::WrapperDescriptor(_), _) => KnownClass::WrapperDescriptorType
|
(Type::WrapperDescriptor(_), _) => KnownClass::WrapperDescriptorType
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
|
|
||||||
(Type::DataclassDecorator(_) | Type::DataclassTransformer(_), _) => {
|
(Type::DataclassDecorator(_) | Type::DataclassTransformer(_), _) => {
|
||||||
// TODO: Implement subtyping using an equivalent `Callable` type.
|
// TODO: Implement subtyping using an equivalent `Callable` type.
|
||||||
|
|
@ -1503,24 +1516,30 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
// `TypeIs` is invariant.
|
// `TypeIs` is invariant.
|
||||||
(Type::TypeIs(left), Type::TypeIs(right)) => {
|
(Type::TypeIs(left), Type::TypeIs(right)) => {
|
||||||
left.return_type(db)
|
left.return_type(db).has_relation_to_impl(
|
||||||
.has_relation_to(db, right.return_type(db), relation)
|
db,
|
||||||
&& right
|
right.return_type(db),
|
||||||
.return_type(db)
|
relation,
|
||||||
.has_relation_to(db, left.return_type(db), relation)
|
visitor,
|
||||||
|
) && right.return_type(db).has_relation_to_impl(
|
||||||
|
db,
|
||||||
|
left.return_type(db),
|
||||||
|
relation,
|
||||||
|
visitor,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `TypeIs[T]` is a subtype of `bool`.
|
// `TypeIs[T]` is a subtype of `bool`.
|
||||||
(Type::TypeIs(_), _) => KnownClass::Bool
|
(Type::TypeIs(_), _) => KnownClass::Bool
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
|
|
||||||
// Function-like callables are subtypes of `FunctionType`
|
// Function-like callables are subtypes of `FunctionType`
|
||||||
(Type::Callable(callable), _)
|
(Type::Callable(callable), _)
|
||||||
if callable.is_function_like(db)
|
if callable.is_function_like(db)
|
||||||
&& KnownClass::FunctionType
|
&& KnownClass::FunctionType
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation) =>
|
.has_relation_to_impl(db, target, relation, visitor) =>
|
||||||
{
|
{
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -1548,7 +1567,7 @@ impl<'db> Type<'db> {
|
||||||
(Type::BoundSuper(_), Type::BoundSuper(_)) => self.is_equivalent_to(db, target),
|
(Type::BoundSuper(_), Type::BoundSuper(_)) => self.is_equivalent_to(db, target),
|
||||||
(Type::BoundSuper(_), _) => KnownClass::Super
|
(Type::BoundSuper(_), _) => KnownClass::Super
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
|
|
||||||
// `Literal[<class 'C'>]` is a subtype of `type[B]` if `C` is a subclass of `B`,
|
// `Literal[<class 'C'>]` is a subtype of `type[B]` if `C` is a subclass of `B`,
|
||||||
// since `type[B]` describes all possible runtime subclasses of the class object `B`.
|
// since `type[B]` describes all possible runtime subclasses of the class object `B`.
|
||||||
|
|
@ -1577,25 +1596,30 @@ impl<'db> Type<'db> {
|
||||||
// is an instance of its metaclass `abc.ABCMeta`.
|
// is an instance of its metaclass `abc.ABCMeta`.
|
||||||
(Type::ClassLiteral(class), _) => class
|
(Type::ClassLiteral(class), _) => class
|
||||||
.metaclass_instance_type(db)
|
.metaclass_instance_type(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
(Type::GenericAlias(alias), _) => ClassType::from(alias)
|
(Type::GenericAlias(alias), _) => ClassType::from(alias)
|
||||||
.metaclass_instance_type(db)
|
.metaclass_instance_type(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
|
|
||||||
// `type[Any]` is a subtype of `type[object]`, and is assignable to any `type[...]`
|
// `type[Any]` is a subtype of `type[object]`, and is assignable to any `type[...]`
|
||||||
(Type::SubclassOf(subclass_of_ty), other) if subclass_of_ty.is_dynamic() => {
|
(Type::SubclassOf(subclass_of_ty), other) if subclass_of_ty.is_dynamic() => {
|
||||||
KnownClass::Type
|
KnownClass::Type
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, other, relation)
|
.has_relation_to_impl(db, other, relation, visitor)
|
||||||
|| (relation.is_assignability()
|
|| (relation.is_assignability()
|
||||||
&& other.has_relation_to(db, KnownClass::Type.to_instance(db), relation))
|
&& other.has_relation_to_impl(
|
||||||
|
db,
|
||||||
|
KnownClass::Type.to_instance(db),
|
||||||
|
relation,
|
||||||
|
visitor,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any `type[...]` type is assignable to `type[Any]`
|
// Any `type[...]` type is assignable to `type[Any]`
|
||||||
(other, Type::SubclassOf(subclass_of_ty))
|
(other, Type::SubclassOf(subclass_of_ty))
|
||||||
if subclass_of_ty.is_dynamic() && relation.is_assignability() =>
|
if subclass_of_ty.is_dynamic() && relation.is_assignability() =>
|
||||||
{
|
{
|
||||||
other.has_relation_to(db, KnownClass::Type.to_instance(db), relation)
|
other.has_relation_to_impl(db, KnownClass::Type.to_instance(db), relation, visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `type[str]` (== `SubclassOf("str")` in ty) describes all possible runtime subclasses
|
// `type[str]` (== `SubclassOf("str")` in ty) describes all possible runtime subclasses
|
||||||
|
|
@ -1610,18 +1634,18 @@ impl<'db> Type<'db> {
|
||||||
.into_class()
|
.into_class()
|
||||||
.map(|class| class.metaclass_instance_type(db))
|
.map(|class| class.metaclass_instance_type(db))
|
||||||
.unwrap_or_else(|| KnownClass::Type.to_instance(db))
|
.unwrap_or_else(|| KnownClass::Type.to_instance(db))
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
|
|
||||||
// For example: `Type::SpecialForm(SpecialFormType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`,
|
// For example: `Type::SpecialForm(SpecialFormType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`,
|
||||||
// because `Type::SpecialForm(SpecialFormType::Type)` is a set with exactly one runtime value in it
|
// because `Type::SpecialForm(SpecialFormType::Type)` is a set with exactly one runtime value in it
|
||||||
// (the symbol `typing.Type`), and that symbol is known to be an instance of `typing._SpecialForm` at runtime.
|
// (the symbol `typing.Type`), and that symbol is known to be an instance of `typing._SpecialForm` at runtime.
|
||||||
(Type::SpecialForm(left), right) => left
|
(Type::SpecialForm(left), right) => left
|
||||||
.instance_fallback(db)
|
.instance_fallback(db)
|
||||||
.has_relation_to(db, right, relation),
|
.has_relation_to_impl(db, right, relation, visitor),
|
||||||
|
|
||||||
(Type::KnownInstance(left), right) => left
|
(Type::KnownInstance(left), right) => left
|
||||||
.instance_fallback(db)
|
.instance_fallback(db)
|
||||||
.has_relation_to(db, right, relation),
|
.has_relation_to_impl(db, right, relation, visitor),
|
||||||
|
|
||||||
// `bool` is a subtype of `int`, because `bool` subclasses `int`,
|
// `bool` is a subtype of `int`, because `bool` subclasses `int`,
|
||||||
// which means that all instances of `bool` are also instances of `int`
|
// which means that all instances of `bool` are also instances of `int`
|
||||||
|
|
@ -1631,10 +1655,13 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
(Type::PropertyInstance(_), _) => KnownClass::Property
|
(Type::PropertyInstance(_), _) => KnownClass::Property
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to(db, target, relation),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
(_, Type::PropertyInstance(_)) => {
|
(_, Type::PropertyInstance(_)) => self.has_relation_to_impl(
|
||||||
self.has_relation_to(db, KnownClass::Property.to_instance(db), relation)
|
db,
|
||||||
}
|
KnownClass::Property.to_instance(db),
|
||||||
|
relation,
|
||||||
|
visitor,
|
||||||
|
),
|
||||||
|
|
||||||
// Other than the special cases enumerated above, `Instance` types and typevars are
|
// Other than the special cases enumerated above, `Instance` types and typevars are
|
||||||
// never subtypes of any other variants
|
// never subtypes of any other variants
|
||||||
|
|
@ -1655,6 +1682,16 @@ impl<'db> Type<'db> {
|
||||||
///
|
///
|
||||||
/// [equivalent to]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent
|
/// [equivalent to]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent
|
||||||
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||||
|
let mut visitor = PairVisitor::new(true);
|
||||||
|
self.is_equivalent_to_impl(db, other, &mut visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_equivalent_to_impl(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
other: Type<'db>,
|
||||||
|
visitor: &mut PairVisitor<'db>,
|
||||||
|
) -> bool {
|
||||||
if self == other {
|
if self == other {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -5441,6 +5478,16 @@ impl<'db> Type<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
) -> Type<'db> {
|
||||||
|
let mut visitor = TypeTransformer::default();
|
||||||
|
self.apply_type_mapping_impl(db, type_mapping, &mut visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_type_mapping_impl<'a>(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
visitor: &mut TypeTransformer<'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
match self {
|
match self {
|
||||||
Type::TypeVar(typevar) => match type_mapping {
|
Type::TypeVar(typevar) => match type_mapping {
|
||||||
|
|
@ -5512,21 +5559,21 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Union(union) => union.map(db, |element| {
|
Type::Union(union) => union.map(db, |element| {
|
||||||
element.apply_type_mapping(db, type_mapping)
|
element.apply_type_mapping_impl(db, type_mapping, visitor)
|
||||||
}),
|
}),
|
||||||
Type::Intersection(intersection) => {
|
Type::Intersection(intersection) => {
|
||||||
let mut builder = IntersectionBuilder::new(db);
|
let mut builder = IntersectionBuilder::new(db);
|
||||||
for positive in intersection.positive(db) {
|
for positive in intersection.positive(db) {
|
||||||
builder =
|
builder =
|
||||||
builder.add_positive(positive.apply_type_mapping(db, type_mapping));
|
builder.add_positive(positive.apply_type_mapping_impl(db, type_mapping, visitor));
|
||||||
}
|
}
|
||||||
for negative in intersection.negative(db) {
|
for negative in intersection.negative(db) {
|
||||||
builder =
|
builder =
|
||||||
builder.add_negative(negative.apply_type_mapping(db, type_mapping));
|
builder.add_negative(negative.apply_type_mapping_impl(db, type_mapping, visitor));
|
||||||
}
|
}
|
||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
Type::Tuple(tuple) => Type::tuple(tuple.apply_type_mapping(db, type_mapping)),
|
Type::Tuple(tuple) => Type::tuple(tuple.apply_type_mapping_impl(db, type_mapping, visitor)),
|
||||||
|
|
||||||
Type::TypeIs(type_is) => type_is.with_type(db, type_is.return_type(db).apply_type_mapping(db, type_mapping)),
|
Type::TypeIs(type_is) => type_is.with_type(db, type_is.return_type(db).apply_type_mapping(db, type_mapping)),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -420,15 +420,25 @@ impl<'db> Specialization<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
) -> Self {
|
||||||
|
let mut visitor = TypeTransformer::default();
|
||||||
|
self.apply_type_mapping_impl(db, type_mapping, &mut visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn apply_type_mapping_impl<'a>(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
visitor: &mut TypeTransformer<'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let types: Box<[_]> = self
|
let types: Box<[_]> = self
|
||||||
.types(db)
|
.types(db)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.apply_type_mapping(db, type_mapping))
|
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor))
|
||||||
.collect();
|
.collect();
|
||||||
let tuple_inner = self
|
let tuple_inner = self
|
||||||
.tuple_inner(db)
|
.tuple_inner(db)
|
||||||
.and_then(|tuple| tuple.apply_type_mapping(db, type_mapping));
|
.and_then(|tuple| tuple.apply_type_mapping_impl(db, type_mapping, visitor));
|
||||||
Specialization::new(db, self.generic_context(db), types, tuple_inner)
|
Specialization::new(db, self.generic_context(db), types, tuple_inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,12 +213,17 @@ impl<'db> TupleType<'db> {
|
||||||
TupleType::new(db, self.tuple(db).materialize(db, variance))
|
TupleType::new(db, self.tuple(db).materialize(db, variance))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_type_mapping<'a>(
|
pub(crate) fn apply_type_mapping_impl<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
visitor: &mut TypeTransformer<'db>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
TupleType::new(db, self.tuple(db).apply_type_mapping(db, type_mapping))
|
TupleType::new(
|
||||||
|
db,
|
||||||
|
self.tuple(db)
|
||||||
|
.apply_type_mapping_impl(db, type_mapping, visitor),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_legacy_typevars(
|
pub(crate) fn find_legacy_typevars(
|
||||||
|
|
@ -376,11 +381,16 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
Self::from_elements(self.0.iter().map(|ty| ty.materialize(db, variance)))
|
Self::from_elements(self.0.iter().map(|ty| ty.materialize(db, variance)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping_impl<'a>(
|
||||||
|
&self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
visitor: &mut TypeTransformer<'db>,
|
||||||
|
) -> Self {
|
||||||
Self::from_elements(
|
Self::from_elements(
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
|
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -706,19 +716,26 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(
|
fn apply_type_mapping_impl<'a>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
visitor: &mut TypeTransformer<'db>,
|
||||||
) -> TupleSpec<'db> {
|
) -> TupleSpec<'db> {
|
||||||
Self::mixed(
|
// TODO maybe use interior mutability in `TypeTransformer` instead of passing around
|
||||||
self.prefix
|
// mutable references to it; then we wouldn't need to collect here.
|
||||||
|
let prefix: Vec<_> = self
|
||||||
|
.prefix
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
|
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor))
|
||||||
self.variable.apply_type_mapping(db, type_mapping),
|
.collect();
|
||||||
|
Self::mixed(
|
||||||
|
prefix,
|
||||||
|
self.variable
|
||||||
|
.apply_type_mapping_impl(db, type_mapping, visitor),
|
||||||
self.suffix
|
self.suffix
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
|
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1058,14 +1075,17 @@ impl<'db> Tuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_type_mapping<'a>(
|
pub(crate) fn apply_type_mapping_impl<'a>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
|
visitor: &mut TypeTransformer<'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Tuple::Fixed(tuple) => Tuple::Fixed(tuple.apply_type_mapping(db, type_mapping)),
|
Tuple::Fixed(tuple) => {
|
||||||
Tuple::Variable(tuple) => tuple.apply_type_mapping(db, type_mapping),
|
Tuple::Fixed(tuple.apply_type_mapping_impl(db, type_mapping, visitor))
|
||||||
|
}
|
||||||
|
Tuple::Variable(tuple) => tuple.apply_type_mapping_impl(db, type_mapping, visitor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue