mirror of https://github.com/astral-sh/ruff
Do the hard things (with some help from Doug!)
This commit is contained in:
parent
671f1358fb
commit
cfc46acc46
|
|
@ -1266,7 +1266,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
class_def_node.type_params.as_ref().map(|type_params| {
|
class_def_node.type_params.as_ref().map(|type_params| {
|
||||||
let index = semantic_index(db, scope.file(db));
|
let index = semantic_index(db, scope.file(db));
|
||||||
let definition = index.expect_single_definition(class_def_node);
|
let definition = index.expect_single_definition(class_def_node);
|
||||||
GenericContext::from_type_params(db, index, definition, type_params)
|
GenericContext::from_type_params(db, index, definition, type_params, self.known(db))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1290,6 +1290,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.filter(|ty| matches!(ty, Type::GenericAlias(_))),
|
.filter(|ty| matches!(ty, Type::GenericAlias(_))),
|
||||||
|
self.known(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1334,8 +1335,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
specialization: Option<Specialization<'db>>,
|
specialization: Option<Specialization<'db>>,
|
||||||
) -> ClassType<'db> {
|
) -> ClassType<'db> {
|
||||||
self.apply_specialization(db, |generic_context| {
|
self.apply_specialization(db, |generic_context| {
|
||||||
specialization
|
specialization.unwrap_or_else(|| generic_context.default_specialization(db))
|
||||||
.unwrap_or_else(|| generic_context.default_specialization(db, self.known(db)))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1344,7 +1344,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
/// 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(db, |generic_context| {
|
self.apply_specialization(db, |generic_context| {
|
||||||
generic_context.default_specialization(db, self.known(db))
|
generic_context.default_specialization(db)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,6 @@ impl Display for DisplayGenericContext<'_> {
|
||||||
let variables = self.generic_context.variables(self.db);
|
let variables = self.generic_context.variables(self.db);
|
||||||
|
|
||||||
let non_implicit_variables: Vec<_> = variables
|
let non_implicit_variables: Vec<_> = variables
|
||||||
.iter()
|
|
||||||
.filter(|bound_typevar| !bound_typevar.typevar(self.db).is_implicit(self.db))
|
.filter(|bound_typevar| !bound_typevar.typevar(self.db).is_implicit(self.db))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -340,7 +340,7 @@ impl<'db> OverloadLiteral<'db> {
|
||||||
let definition = self.definition(db);
|
let definition = self.definition(db);
|
||||||
let generic_context = function_stmt_node.type_params.as_ref().map(|type_params| {
|
let generic_context = function_stmt_node.type_params.as_ref().map(|type_params| {
|
||||||
let index = semantic_index(db, scope.file(db));
|
let index = semantic_index(db, scope.file(db));
|
||||||
GenericContext::from_type_params(db, index, definition, type_params)
|
GenericContext::from_type_params(db, index, definition, type_params, None)
|
||||||
});
|
});
|
||||||
|
|
||||||
let index = semantic_index(db, scope.file(db));
|
let index = semantic_index(db, scope.file(db));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use itertools::Either;
|
||||||
use ruff_db::parsed::ParsedModuleRef;
|
use ruff_db::parsed::ParsedModuleRef;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
@ -12,7 +13,7 @@ use crate::types::class_base::ClassBase;
|
||||||
use crate::types::infer::infer_definition_types;
|
use crate::types::infer::infer_definition_types;
|
||||||
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
use crate::types::tuple::{TupleSpec, TupleType};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, HasRelationToVisitor, KnownClass,
|
ApplyTypeMappingVisitor, BoundTypeVarInstance, HasRelationToVisitor, KnownClass,
|
||||||
KnownInstanceType, NormalizedVisitor, Type, TypeMapping, TypeRelation, TypeTransformer,
|
KnownInstanceType, NormalizedVisitor, Type, TypeMapping, TypeRelation, TypeTransformer,
|
||||||
|
|
@ -82,6 +83,14 @@ pub(crate) fn bind_typevar<'db>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum GenericContextInner<'db> {
|
||||||
|
Tuple {
|
||||||
|
single_typevar: BoundTypeVarInstance<'db>,
|
||||||
|
},
|
||||||
|
NonTuple(FxOrderSet<BoundTypeVarInstance<'db>>),
|
||||||
|
}
|
||||||
|
|
||||||
/// A list of formal type variables for a generic function, class, or type alias.
|
/// A list of formal type variables for a generic function, class, or type alias.
|
||||||
///
|
///
|
||||||
/// TODO: Handle nested generic contexts better, with actual parent links to the lexically
|
/// TODO: Handle nested generic contexts better, with actual parent links to the lexically
|
||||||
|
|
@ -94,7 +103,7 @@ pub(crate) fn bind_typevar<'db>(
|
||||||
#[derive(PartialOrd, Ord)]
|
#[derive(PartialOrd, Ord)]
|
||||||
pub struct GenericContext<'db> {
|
pub struct GenericContext<'db> {
|
||||||
#[returns(ref)]
|
#[returns(ref)]
|
||||||
pub(crate) variables: FxOrderSet<BoundTypeVarInstance<'db>>,
|
pub(crate) inner: GenericContextInner<'db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
|
|
@ -103,7 +112,7 @@ pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?S
|
||||||
visitor: &V,
|
visitor: &V,
|
||||||
) {
|
) {
|
||||||
for bound_typevar in context.variables(db) {
|
for bound_typevar in context.variables(db) {
|
||||||
visitor.visit_bound_type_var_type(db, *bound_typevar);
|
visitor.visit_bound_type_var_type(db, bound_typevar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,12 +120,25 @@ pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?S
|
||||||
impl get_size2::GetSize for GenericContext<'_> {}
|
impl get_size2::GetSize for GenericContext<'_> {}
|
||||||
|
|
||||||
impl<'db> GenericContext<'db> {
|
impl<'db> GenericContext<'db> {
|
||||||
|
pub(crate) fn variables(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
) -> impl ExactSizeIterator<Item = BoundTypeVarInstance<'db>> {
|
||||||
|
match self.inner(db) {
|
||||||
|
GenericContextInner::Tuple { single_typevar } => {
|
||||||
|
Either::Left(std::iter::once(*single_typevar))
|
||||||
|
}
|
||||||
|
GenericContextInner::NonTuple(variables) => Either::Right(variables.iter().copied()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a generic context from a list of PEP-695 type parameters.
|
/// Creates a generic context from a list of PEP-695 type parameters.
|
||||||
pub(crate) fn from_type_params(
|
pub(crate) fn from_type_params(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
index: &'db SemanticIndex<'db>,
|
index: &'db SemanticIndex<'db>,
|
||||||
binding_context: Definition<'db>,
|
binding_context: Definition<'db>,
|
||||||
type_params_node: &ast::TypeParams,
|
type_params_node: &ast::TypeParams,
|
||||||
|
known_class: Option<KnownClass>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let variables: FxOrderSet<_> = type_params_node
|
let variables: FxOrderSet<_> = type_params_node
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -124,7 +146,23 @@ impl<'db> GenericContext<'db> {
|
||||||
Self::variable_from_type_param(db, index, binding_context, type_param)
|
Self::variable_from_type_param(db, index, binding_context, type_param)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Self::new(db, variables)
|
|
||||||
|
match known_class {
|
||||||
|
Some(KnownClass::Tuple) => {
|
||||||
|
assert_eq!(
|
||||||
|
variables.len(),
|
||||||
|
1,
|
||||||
|
"Tuple should always have exactly one typevar"
|
||||||
|
);
|
||||||
|
Self::new(
|
||||||
|
db,
|
||||||
|
GenericContextInner::Tuple {
|
||||||
|
single_typevar: variables[0],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Self::new(db, GenericContextInner::NonTuple(variables)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable_from_type_param(
|
fn variable_from_type_param(
|
||||||
|
|
@ -174,7 +212,7 @@ impl<'db> GenericContext<'db> {
|
||||||
if variables.is_empty() {
|
if variables.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(Self::new(db, variables))
|
Some(Self::new(db, GenericContextInner::NonTuple(variables)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a generic context from the legacy `TypeVar`s that appear in class's base class
|
/// Creates a generic context from the legacy `TypeVar`s that appear in class's base class
|
||||||
|
|
@ -182,6 +220,7 @@ impl<'db> GenericContext<'db> {
|
||||||
pub(crate) fn from_base_classes(
|
pub(crate) fn from_base_classes(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
bases: impl Iterator<Item = Type<'db>>,
|
bases: impl Iterator<Item = Type<'db>>,
|
||||||
|
known_class: Option<KnownClass>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let mut variables = FxOrderSet::default();
|
let mut variables = FxOrderSet::default();
|
||||||
for base in bases {
|
for base in bases {
|
||||||
|
|
@ -190,7 +229,23 @@ impl<'db> GenericContext<'db> {
|
||||||
if variables.is_empty() {
|
if variables.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(Self::new(db, variables))
|
let context = match known_class {
|
||||||
|
Some(KnownClass::Tuple) => {
|
||||||
|
assert_eq!(
|
||||||
|
variables.len(),
|
||||||
|
1,
|
||||||
|
"Tuple should always have exactly one typevar"
|
||||||
|
);
|
||||||
|
Self::new(
|
||||||
|
db,
|
||||||
|
GenericContextInner::Tuple {
|
||||||
|
single_typevar: variables[0],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Self::new(db, GenericContextInner::NonTuple(variables)),
|
||||||
|
};
|
||||||
|
Some(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn len(self, db: &'db dyn Db) -> usize {
|
pub(crate) fn len(self, db: &'db dyn Db) -> usize {
|
||||||
|
|
@ -200,8 +255,7 @@ impl<'db> GenericContext<'db> {
|
||||||
pub(crate) fn signature(self, db: &'db dyn Db) -> Signature<'db> {
|
pub(crate) fn signature(self, db: &'db dyn Db) -> Signature<'db> {
|
||||||
let parameters = Parameters::new(
|
let parameters = Parameters::new(
|
||||||
self.variables(db)
|
self.variables(db)
|
||||||
.iter()
|
.map(|typevar| Self::parameter_from_typevar(db, typevar)),
|
||||||
.map(|typevar| Self::parameter_from_typevar(db, *typevar)),
|
|
||||||
);
|
);
|
||||||
Signature::new(parameters, None)
|
Signature::new(parameters, None)
|
||||||
}
|
}
|
||||||
|
|
@ -231,50 +285,54 @@ impl<'db> GenericContext<'db> {
|
||||||
parameter
|
parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_specialization(
|
pub(crate) fn default_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||||
self,
|
self.specialize_partial(db, &vec![None; self.variables(db).len()])
|
||||||
db: &'db dyn Db,
|
|
||||||
known_class: Option<KnownClass>,
|
|
||||||
) -> Specialization<'db> {
|
|
||||||
let partial = self.specialize_partial(db, &vec![None; self.variables(db).len()]);
|
|
||||||
if known_class == Some(KnownClass::Tuple) {
|
|
||||||
Specialization::new(
|
|
||||||
db,
|
|
||||||
self,
|
|
||||||
partial.types(db),
|
|
||||||
Some(TupleType::homogeneous(db, Type::unknown())),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
partial
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||||
let types = self
|
let types = self.variables(db).map(Type::TypeVar).collect();
|
||||||
.variables(db)
|
|
||||||
.iter()
|
|
||||||
.map(|typevar| Type::TypeVar(*typevar))
|
|
||||||
.collect();
|
|
||||||
self.specialize(db, types)
|
self.specialize(db, types)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unknown_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
pub(crate) fn unknown_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||||
let types = vec![Type::unknown(); self.variables(db).len()];
|
let types = vec![Type::unknown(); self.variables(db).len()];
|
||||||
self.specialize(db, types.into())
|
self.specialize(db, types.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tuple type of the typevars introduced by this generic context.
|
/// Returns a tuple type of the typevars introduced by this generic context.
|
||||||
pub(crate) fn as_tuple(self, db: &'db dyn Db) -> Type<'db> {
|
pub(crate) fn as_tuple(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
Type::heterogeneous_tuple(
|
Type::heterogeneous_tuple(db, self.variables(db).map(Type::TypeVar))
|
||||||
db,
|
|
||||||
self.variables(db)
|
|
||||||
.iter()
|
|
||||||
.map(|typevar| Type::TypeVar(*typevar)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_subset_of(self, db: &'db dyn Db, other: GenericContext<'db>) -> bool {
|
pub(crate) fn is_subset_of(self, db: &'db dyn Db, other: GenericContext<'db>) -> bool {
|
||||||
self.variables(db).is_subset(other.variables(db))
|
match (self.inner(db), other.inner(db)) {
|
||||||
|
(GenericContextInner::NonTuple(left), GenericContextInner::NonTuple(right)) => {
|
||||||
|
left.is_subset(right)
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
GenericContextInner::Tuple {
|
||||||
|
single_typevar: left,
|
||||||
|
},
|
||||||
|
GenericContextInner::NonTuple(right),
|
||||||
|
) => right.contains(left),
|
||||||
|
|
||||||
|
(
|
||||||
|
GenericContextInner::NonTuple(left),
|
||||||
|
GenericContextInner::Tuple {
|
||||||
|
single_typevar: right,
|
||||||
|
},
|
||||||
|
) => left.len() == 1 && left[0] == *right,
|
||||||
|
|
||||||
|
(
|
||||||
|
GenericContextInner::Tuple {
|
||||||
|
single_typevar: left,
|
||||||
|
},
|
||||||
|
GenericContextInner::Tuple {
|
||||||
|
single_typevar: right,
|
||||||
|
},
|
||||||
|
) => left == right,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn binds_typevar(
|
pub(crate) fn binds_typevar(
|
||||||
|
|
@ -283,9 +341,7 @@ impl<'db> GenericContext<'db> {
|
||||||
typevar: TypeVarInstance<'db>,
|
typevar: TypeVarInstance<'db>,
|
||||||
) -> Option<BoundTypeVarInstance<'db>> {
|
) -> Option<BoundTypeVarInstance<'db>> {
|
||||||
self.variables(db)
|
self.variables(db)
|
||||||
.iter()
|
|
||||||
.find(|self_bound_typevar| self_bound_typevar.typevar(db) == typevar)
|
.find(|self_bound_typevar| self_bound_typevar.typevar(db) == typevar)
|
||||||
.copied()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a specialization of this generic context. Panics if the length of `types` does not
|
/// Creates a specialization of this generic context. Panics if the length of `types` does not
|
||||||
|
|
@ -297,18 +353,25 @@ impl<'db> GenericContext<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
types: Box<[Type<'db>]>,
|
types: Box<[Type<'db>]>,
|
||||||
) -> Specialization<'db> {
|
) -> Specialization<'db> {
|
||||||
assert!(self.variables(db).len() == types.len());
|
assert_eq!(self.variables(db).len(), types.len());
|
||||||
Specialization::new(db, self, types, None)
|
debug_assert!(
|
||||||
|
matches!(self.inner(db), GenericContextInner::NonTuple(_)),
|
||||||
|
"Should never call `GenericContext::specialize` on a tuple context"
|
||||||
|
);
|
||||||
|
Specialization::new(db, self, SpecializationInner::NonTuple(types))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a specialization of this generic context for the `tuple` class.
|
/// Creates a specialization of this generic context for the `tuple` class.
|
||||||
pub(crate) fn specialize_tuple(
|
pub(crate) fn specialize_tuple(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
element_type: Type<'db>,
|
|
||||||
tuple: TupleType<'db>,
|
tuple: TupleType<'db>,
|
||||||
) -> Specialization<'db> {
|
) -> Specialization<'db> {
|
||||||
Specialization::new(db, self, Box::from([element_type]), Some(tuple))
|
debug_assert!(
|
||||||
|
matches!(self.inner(db), GenericContextInner::Tuple { .. }),
|
||||||
|
"Should never call `GenericContext::specialize_tuple` on a non-tuple context"
|
||||||
|
);
|
||||||
|
Specialization::new(db, self, SpecializationInner::Tuple(tuple))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a specialization of this generic context. Panics if the length of `types` does not
|
/// Creates a specialization of this generic context. Panics if the length of `types` does not
|
||||||
|
|
@ -319,8 +382,14 @@ impl<'db> GenericContext<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
types: &[Option<Type<'db>>],
|
types: &[Option<Type<'db>>],
|
||||||
) -> Specialization<'db> {
|
) -> Specialization<'db> {
|
||||||
|
if let GenericContextInner::Tuple { .. } = self.inner(db) {
|
||||||
|
assert_eq!(types.len(), 1);
|
||||||
|
let ty = types[0].unwrap_or_else(Type::unknown);
|
||||||
|
return self.specialize_tuple(db, TupleType::homogeneous(db, ty));
|
||||||
|
}
|
||||||
|
|
||||||
let variables = self.variables(db);
|
let variables = self.variables(db);
|
||||||
assert!(variables.len() == types.len());
|
assert_eq!(variables.len(), types.len());
|
||||||
|
|
||||||
// Typevars can have other typevars as their default values, e.g.
|
// Typevars can have other typevars as their default values, e.g.
|
||||||
//
|
//
|
||||||
|
|
@ -353,21 +422,41 @@ impl<'db> GenericContext<'db> {
|
||||||
expanded[idx] = default;
|
expanded[idx] = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
Specialization::new(db, self, expanded.into_boxed_slice(), None)
|
Specialization::new(
|
||||||
|
db,
|
||||||
|
self,
|
||||||
|
SpecializationInner::NonTuple(expanded.into_boxed_slice()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
let variables: FxOrderSet<_> = self
|
let inner = match self.inner(db) {
|
||||||
.variables(db)
|
GenericContextInner::Tuple { single_typevar } => {
|
||||||
.iter()
|
let single_typevar = single_typevar.normalized_impl(db, visitor);
|
||||||
|
GenericContextInner::Tuple { single_typevar }
|
||||||
|
}
|
||||||
|
GenericContextInner::NonTuple(variables) => {
|
||||||
|
let variables: FxOrderSet<_> = variables
|
||||||
|
.into_iter()
|
||||||
.map(|bound_typevar| bound_typevar.normalized_impl(db, visitor))
|
.map(|bound_typevar| bound_typevar.normalized_impl(db, visitor))
|
||||||
.collect();
|
.collect();
|
||||||
Self::new(db, variables)
|
GenericContextInner::NonTuple(variables)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::new(db, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn heap_size((variables,): &(FxOrderSet<BoundTypeVarInstance<'db>>,)) -> usize {
|
fn heap_size((inner,): &(GenericContextInner<'db>,)) -> usize {
|
||||||
|
match inner {
|
||||||
|
GenericContextInner::Tuple { single_typevar } => {
|
||||||
|
ruff_memory_usage::heap_size(single_typevar)
|
||||||
|
}
|
||||||
|
GenericContextInner::NonTuple(variables) => {
|
||||||
ruff_memory_usage::order_set_heap_size(variables)
|
ruff_memory_usage::order_set_heap_size(variables)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -392,7 +481,7 @@ impl std::fmt::Display for LegacyGenericBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||||
enum SpecializationInner<'db> {
|
pub enum SpecializationInner<'db> {
|
||||||
Tuple(TupleType<'db>),
|
Tuple(TupleType<'db>),
|
||||||
NonTuple(Box<[Type<'db>]>),
|
NonTuple(Box<[Type<'db>]>),
|
||||||
}
|
}
|
||||||
|
|
@ -423,7 +512,7 @@ pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Si
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Specialization<'db> {
|
impl<'db> Specialization<'db> {
|
||||||
pub(crate) fn types(self, db: &'db dyn Db) -> &[Type<'db>] {
|
pub(crate) fn types(self, db: &'db dyn Db) -> &'db [Type<'db>] {
|
||||||
#[salsa::tracked(returns(ref))]
|
#[salsa::tracked(returns(ref))]
|
||||||
fn homogeneous_element_type<'db>(db: &'db dyn Db, tuple: TupleType<'db>) -> Type<'db> {
|
fn homogeneous_element_type<'db>(db: &'db dyn Db, tuple: TupleType<'db>) -> Type<'db> {
|
||||||
tuple.tuple(db).homogeneous_element_type(db)
|
tuple.tuple(db).homogeneous_element_type(db)
|
||||||
|
|
@ -452,12 +541,19 @@ impl<'db> Specialization<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
bound_typevar: BoundTypeVarInstance<'db>,
|
bound_typevar: BoundTypeVarInstance<'db>,
|
||||||
) -> Option<Type<'db>> {
|
) -> Option<Type<'db>> {
|
||||||
let index = self
|
match self.generic_context(db).inner(db) {
|
||||||
.generic_context(db)
|
GenericContextInner::Tuple { single_typevar } => (*single_typevar == bound_typevar)
|
||||||
.variables(db)
|
.then(|| {
|
||||||
.get_index_of(&bound_typevar)?;
|
let types = self.types(db);
|
||||||
|
assert_eq!(types.len(), 1);
|
||||||
|
types[0]
|
||||||
|
}),
|
||||||
|
GenericContextInner::NonTuple(variables) => {
|
||||||
|
let index = variables.get_index_of(&bound_typevar)?;
|
||||||
self.types(db).get(index).copied()
|
self.types(db).get(index).copied()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies a specialization to this specialization. This is used, for instance, when a generic
|
/// Applies a specialization to this specialization. This is used, for instance, when a generic
|
||||||
/// class inherits from a generic alias:
|
/// class inherits from a generic alias:
|
||||||
|
|
@ -526,7 +622,13 @@ impl<'db> Specialization<'db> {
|
||||||
/// Panics if the two specializations are not for the same generic context.
|
/// Panics if the two specializations are not for the same generic context.
|
||||||
pub(crate) fn combine(self, db: &'db dyn Db, other: Self) -> Self {
|
pub(crate) fn combine(self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
let generic_context = self.generic_context(db);
|
let generic_context = self.generic_context(db);
|
||||||
assert!(other.generic_context(db) == generic_context);
|
|
||||||
|
assert_eq!(other.generic_context(db), generic_context);
|
||||||
|
debug_assert!(
|
||||||
|
matches!(self.inner(db), SpecializationInner::NonTuple(_)),
|
||||||
|
"The tuple constructor is special-cased everywhere"
|
||||||
|
);
|
||||||
|
|
||||||
// TODO special-casing Unknown to mean "no mapping" is not right here, and can give
|
// TODO special-casing Unknown to mean "no mapping" is not right here, and can give
|
||||||
// confusing/wrong results in cases where there was a mapping found for a typevar, and it
|
// confusing/wrong results in cases where there was a mapping found for a typevar, and it
|
||||||
// was of type Unknown. We should probably add a bitset or similar to Specialization that
|
// was of type Unknown. We should probably add a bitset or similar to Specialization that
|
||||||
|
|
@ -540,8 +642,12 @@ impl<'db> Specialization<'db> {
|
||||||
_ => UnionType::from_elements(db, [self_type, other_type]),
|
_ => UnionType::from_elements(db, [self_type, other_type]),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// TODO: Combine the tuple specs too
|
|
||||||
Specialization::new(db, self.generic_context(db), types, None)
|
Specialization::new(
|
||||||
|
db,
|
||||||
|
self.generic_context(db),
|
||||||
|
SpecializationInner::NonTuple(types),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
|
|
@ -564,11 +670,17 @@ impl<'db> Specialization<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||||
|
let inner = match self.inner(db) {
|
||||||
|
SpecializationInner::Tuple(tuple) => SpecializationInner::Tuple(
|
||||||
|
tuple
|
||||||
|
.materialize(db, variance)
|
||||||
|
.unwrap_or_else(|| TupleType::empty(db)),
|
||||||
|
),
|
||||||
|
SpecializationInner::NonTuple(types) => {
|
||||||
let types: Box<[_]> = self
|
let types: Box<[_]> = self
|
||||||
.generic_context(db)
|
.generic_context(db)
|
||||||
.variables(db)
|
.variables(db)
|
||||||
.into_iter()
|
.zip(types)
|
||||||
.zip(self.types(db))
|
|
||||||
.map(|(bound_typevar, vartype)| {
|
.map(|(bound_typevar, vartype)| {
|
||||||
let variance = match bound_typevar.typevar(db).variance(db) {
|
let variance = match bound_typevar.typevar(db).variance(db) {
|
||||||
TypeVarVariance::Invariant => TypeVarVariance::Invariant,
|
TypeVarVariance::Invariant => TypeVarVariance::Invariant,
|
||||||
|
|
@ -579,11 +691,11 @@ impl<'db> Specialization<'db> {
|
||||||
vartype.materialize(db, variance)
|
vartype.materialize(db, variance)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let tuple_inner = self.tuple_inner(db).and_then(|tuple| {
|
SpecializationInner::NonTuple(types)
|
||||||
// Tuples are immutable, so tuple element types are always in covariant position.
|
}
|
||||||
tuple.materialize(db, variance)
|
};
|
||||||
});
|
|
||||||
Specialization::new(db, self.generic_context(db), types, tuple_inner)
|
Specialization::new(db, self.generic_context(db), inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn has_relation_to_impl(
|
pub(crate) fn has_relation_to_impl(
|
||||||
|
|
@ -598,12 +710,14 @@ impl<'db> Specialization<'db> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(self_tuple), Some(other_tuple)) = (self.tuple_inner(db), other.tuple_inner(db))
|
if let (SpecializationInner::Tuple(left), SpecializationInner::Tuple(right)) =
|
||||||
|
(self.inner(db), other.inner(db))
|
||||||
{
|
{
|
||||||
return self_tuple.has_relation_to_impl(db, other_tuple, relation, visitor);
|
return left.has_relation_to_impl(db, *right, relation, visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
for ((bound_typevar, self_type), other_type) in generic_context
|
||||||
|
.variables(db)
|
||||||
.zip(self.types(db))
|
.zip(self.types(db))
|
||||||
.zip(other.types(db))
|
.zip(other.types(db))
|
||||||
{
|
{
|
||||||
|
|
@ -650,7 +764,8 @@ impl<'db> Specialization<'db> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
for ((bound_typevar, self_type), other_type) in generic_context
|
||||||
|
.variables(db)
|
||||||
.zip(self.types(db))
|
.zip(self.types(db))
|
||||||
.zip(other.types(db))
|
.zip(other.types(db))
|
||||||
{
|
{
|
||||||
|
|
@ -724,12 +839,20 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
bound_typevar: BoundTypeVarInstance<'db>,
|
bound_typevar: BoundTypeVarInstance<'db>,
|
||||||
) -> Option<Type<'db>> {
|
) -> Option<Type<'db>> {
|
||||||
let index = self
|
match self.generic_context.inner(db) {
|
||||||
.generic_context
|
GenericContextInner::Tuple { single_typevar } => {
|
||||||
.variables(db)
|
if bound_typevar == *single_typevar {
|
||||||
.get_index_of(&bound_typevar)?;
|
self.types.first().copied()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GenericContextInner::NonTuple(variables) => {
|
||||||
|
let index = variables.get_index_of(&bound_typevar)?;
|
||||||
self.types.get(index).copied()
|
self.types.get(index).copied()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn to_owned(&self) -> PartialSpecialization<'db, 'db> {
|
pub(crate) fn to_owned(&self) -> PartialSpecialization<'db, 'db> {
|
||||||
PartialSpecialization {
|
PartialSpecialization {
|
||||||
|
|
@ -773,18 +896,30 @@ impl<'db> SpecializationBuilder<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build(&mut self, generic_context: GenericContext<'db>) -> Specialization<'db> {
|
pub(crate) fn build(&mut self, generic_context: GenericContext<'db>) -> Specialization<'db> {
|
||||||
let types: Box<[_]> = generic_context
|
let inner = match generic_context.inner(self.db) {
|
||||||
.variables(self.db)
|
GenericContextInner::Tuple { single_typevar } => {
|
||||||
.iter()
|
let ty = self
|
||||||
.map(|variable| {
|
.types
|
||||||
self.types
|
.get(single_typevar)
|
||||||
.get(variable)
|
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(variable.default_type(self.db).unwrap_or(Type::unknown()))
|
.unwrap_or_else(Type::unknown);
|
||||||
|
SpecializationInner::Tuple(TupleType::homogeneous(self.db, ty))
|
||||||
|
}
|
||||||
|
GenericContextInner::NonTuple(variables) => {
|
||||||
|
let types: Box<[_]> = variables
|
||||||
|
.iter()
|
||||||
|
.map(|bound_typevar| {
|
||||||
|
self.types.get(bound_typevar).copied().unwrap_or(
|
||||||
|
bound_typevar
|
||||||
|
.default_type(self.db)
|
||||||
|
.unwrap_or_else(Type::unknown),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// TODO Infer the tuple spec for a tuple type
|
SpecializationInner::NonTuple(types)
|
||||||
Specialization::new(self.db, generic_context, types, None)
|
}
|
||||||
|
};
|
||||||
|
Specialization::new(self.db, generic_context, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
|
fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ use crate::types::enums::is_enum_class;
|
||||||
use crate::types::function::{
|
use crate::types::function::{
|
||||||
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
||||||
};
|
};
|
||||||
use crate::types::generics::{GenericContext, bind_typevar};
|
use crate::types::generics::{GenericContext, GenericContextInner, bind_typevar};
|
||||||
use crate::types::instance::SliceLiteral;
|
use crate::types::instance::SliceLiteral;
|
||||||
use crate::types::mro::MroErrorKind;
|
use crate::types::mro::MroErrorKind;
|
||||||
use crate::types::signatures::{CallableSignature, Signature};
|
use crate::types::signatures::{CallableSignature, Signature};
|
||||||
|
|
@ -9061,7 +9061,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
typevars.map(|typevars| GenericContext::new(self.db(), typevars))
|
typevars
|
||||||
|
.map(|typevars| GenericContext::new(self.db(), GenericContextInner::NonTuple(typevars)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_slice_expression(&mut self, slice: &ast::ExprSlice) -> Type<'db> {
|
fn infer_slice_expression(&mut self, slice: &ast::ExprSlice) -> Type<'db> {
|
||||||
|
|
|
||||||
|
|
@ -131,16 +131,6 @@ pub struct TupleType<'db> {
|
||||||
pub(crate) tuple: TupleSpec<'db>,
|
pub(crate) tuple: TupleSpec<'db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn walk_tuple_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
|
||||||
db: &'db dyn Db,
|
|
||||||
tuple: TupleType<'db>,
|
|
||||||
visitor: &V,
|
|
||||||
) {
|
|
||||||
for element in tuple.tuple(db).all_elements() {
|
|
||||||
visitor.visit_type(db, *element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Salsa heap is tracked separately.
|
// The Salsa heap is tracked separately.
|
||||||
impl get_size2::GetSize for TupleType<'_> {}
|
impl get_size2::GetSize for TupleType<'_> {}
|
||||||
|
|
||||||
|
|
@ -206,10 +196,9 @@ impl<'db> TupleType<'db> {
|
||||||
|
|
||||||
tuple_class.apply_specialization(db, |generic_context| {
|
tuple_class.apply_specialization(db, |generic_context| {
|
||||||
if generic_context.variables(db).len() == 1 {
|
if generic_context.variables(db).len() == 1 {
|
||||||
let element_type = self.tuple(db).homogeneous_element_type(db);
|
generic_context.specialize_tuple(db, self)
|
||||||
generic_context.specialize_tuple(db, element_type, self)
|
|
||||||
} else {
|
} else {
|
||||||
generic_context.default_specialization(db, Some(KnownClass::Tuple))
|
generic_context.default_specialization(db)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -290,9 +279,9 @@ fn to_class_type_cycle_initial<'db>(db: &'db dyn Db, self_: TupleType<'db>) -> C
|
||||||
|
|
||||||
tuple_class.apply_specialization(db, |generic_context| {
|
tuple_class.apply_specialization(db, |generic_context| {
|
||||||
if generic_context.variables(db).len() == 1 {
|
if generic_context.variables(db).len() == 1 {
|
||||||
generic_context.specialize_tuple(db, Type::Never, self_)
|
generic_context.specialize_tuple(db, self_)
|
||||||
} else {
|
} else {
|
||||||
generic_context.default_specialization(db, Some(KnownClass::Tuple))
|
generic_context.default_specialization(db)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue