mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Use separate Rust types for bound and unbound type variables (#19796)
This PR creates separate Rust types for bound and unbound type variables, as proposed in https://github.com/astral-sh/ty/issues/926. Closes https://github.com/astral-sh/ty/issues/926 --------- Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
parent
f3f4db7104
commit
dc84645c36
23 changed files with 846 additions and 560 deletions
|
@ -47,8 +47,8 @@ use crate::types::function::{
|
|||
DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction,
|
||||
};
|
||||
use crate::types::generics::{
|
||||
GenericContext, PartialSpecialization, Specialization, bind_legacy_typevar,
|
||||
walk_generic_context, walk_partial_specialization, walk_specialization,
|
||||
GenericContext, PartialSpecialization, Specialization, bind_typevar, walk_generic_context,
|
||||
walk_partial_specialization, walk_specialization,
|
||||
};
|
||||
pub use crate::types::ide_support::{
|
||||
CallSignatureDetails, Member, all_members, call_signature_details, definition_kind_for_name,
|
||||
|
@ -430,13 +430,14 @@ impl<'db> PropertyInstanceType<'db> {
|
|||
fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
if let Some(ty) = self.getter(db) {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
if let Some(ty) = self.setter(db) {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,7 +601,7 @@ pub enum Type<'db> {
|
|||
Tuple(TupleType<'db>),
|
||||
/// An instance of a typevar in a generic class or function. When the generic class or function
|
||||
/// is specialized, we will replace this typevar with its specialization.
|
||||
TypeVar(TypeVarInstance<'db>),
|
||||
TypeVar(BoundTypeVarInstance<'db>),
|
||||
/// A bound super object like `super()` or `super(A, A())`
|
||||
/// This type doesn't handle an unbound super object like `super(A)`; for that we just use
|
||||
/// a `Type::NominalInstance` of `builtins.super`.
|
||||
|
@ -699,15 +700,18 @@ impl<'db> Type<'db> {
|
|||
// existential type representing "all lists, containing any type." We currently
|
||||
// represent this by replacing `Any` in invariant position with an unresolved type
|
||||
// variable.
|
||||
TypeVarVariance::Invariant => Type::TypeVar(TypeVarInstance::new(
|
||||
TypeVarVariance::Invariant => Type::TypeVar(BoundTypeVarInstance::new(
|
||||
db,
|
||||
Name::new_static("T_all"),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
variance,
|
||||
None,
|
||||
TypeVarKind::Pep695,
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
Name::new_static("T_all"),
|
||||
None,
|
||||
None,
|
||||
variance,
|
||||
None,
|
||||
TypeVarKind::Pep695,
|
||||
),
|
||||
BindingContext::Synthetic,
|
||||
)),
|
||||
TypeVarVariance::Covariant => Type::object(db),
|
||||
TypeVarVariance::Contravariant => Type::Never,
|
||||
|
@ -779,7 +783,7 @@ impl<'db> Type<'db> {
|
|||
)
|
||||
.build(),
|
||||
Type::Tuple(tuple_type) => Type::tuple(tuple_type.materialize(db, variance)),
|
||||
Type::TypeVar(type_var) => Type::TypeVar(type_var.materialize(db, variance)),
|
||||
Type::TypeVar(bound_typevar) => Type::TypeVar(bound_typevar.materialize(db, variance)),
|
||||
Type::TypeIs(type_is) => {
|
||||
type_is.with_type(db, type_is.return_type(db).materialize(db, variance))
|
||||
}
|
||||
|
@ -1082,9 +1086,9 @@ impl<'db> Type<'db> {
|
|||
Type::SubclassOf(subclass_of) => visitor.visit(self, |v| {
|
||||
Type::SubclassOf(subclass_of.normalized_impl(db, v))
|
||||
}),
|
||||
Type::TypeVar(typevar) => {
|
||||
visitor.visit(self, |v| Type::TypeVar(typevar.normalized_impl(db, v)))
|
||||
}
|
||||
Type::TypeVar(bound_typevar) => visitor.visit(self, |v| {
|
||||
Type::TypeVar(bound_typevar.normalized_impl(db, v))
|
||||
}),
|
||||
Type::KnownInstance(known_instance) => visitor.visit(self, |v| {
|
||||
Type::KnownInstance(known_instance.normalized_impl(db, v))
|
||||
}),
|
||||
|
@ -1354,8 +1358,8 @@ impl<'db> Type<'db> {
|
|||
//
|
||||
// Note that this is not handled by the early return at the beginning of this method,
|
||||
// since subtyping between a TypeVar and an arbitrary other type cannot be guaranteed to be reflexive.
|
||||
(Type::TypeVar(lhs_typevar), Type::TypeVar(rhs_typevar))
|
||||
if lhs_typevar == rhs_typevar =>
|
||||
(Type::TypeVar(lhs_bound_typevar), Type::TypeVar(rhs_bound_typevar))
|
||||
if lhs_bound_typevar == rhs_bound_typevar =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
@ -1363,8 +1367,10 @@ impl<'db> Type<'db> {
|
|||
// A fully static typevar is a subtype of its upper bound, and to something similar to
|
||||
// the union of its constraints. An unbound, unconstrained, fully static typevar has an
|
||||
// implicit upper bound of `object` (which is handled above).
|
||||
(Type::TypeVar(typevar), _) if typevar.bound_or_constraints(db).is_some() => {
|
||||
match typevar.bound_or_constraints(db) {
|
||||
(Type::TypeVar(bound_typevar), _)
|
||||
if bound_typevar.typevar(db).bound_or_constraints(db).is_some() =>
|
||||
{
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => unreachable!(),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
bound.has_relation_to(db, target, relation)
|
||||
|
@ -1379,12 +1385,15 @@ impl<'db> Type<'db> {
|
|||
// If the typevar is constrained, there must be multiple constraints, and the typevar
|
||||
// might be specialized to any one of them. However, the constraints do not have to be
|
||||
// disjoint, which means an lhs type might be a subtype of all of the constraints.
|
||||
(_, Type::TypeVar(typevar))
|
||||
if typevar.constraints(db).is_some_and(|constraints| {
|
||||
constraints
|
||||
.iter()
|
||||
.all(|constraint| self.has_relation_to(db, *constraint, relation))
|
||||
}) =>
|
||||
(_, Type::TypeVar(bound_typevar))
|
||||
if bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.is_some_and(|constraints| {
|
||||
constraints
|
||||
.iter()
|
||||
.all(|constraint| self.has_relation_to(db, *constraint, relation))
|
||||
}) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
@ -1802,8 +1811,8 @@ impl<'db> Type<'db> {
|
|||
// be specialized to the same type. (This is an important difference between typevars
|
||||
// and `Any`!) Different typevars might be disjoint, depending on their bounds and
|
||||
// constraints, which are handled below.
|
||||
(Type::TypeVar(self_typevar), Type::TypeVar(other_typevar))
|
||||
if self_typevar == other_typevar =>
|
||||
(Type::TypeVar(self_bound_typevar), Type::TypeVar(other_bound_typevar))
|
||||
if self_bound_typevar == other_bound_typevar =>
|
||||
{
|
||||
false
|
||||
}
|
||||
|
@ -1819,8 +1828,8 @@ impl<'db> Type<'db> {
|
|||
// specialized to any type. A bounded typevar is not disjoint from its bound, and is
|
||||
// only disjoint from other types if its bound is. A constrained typevar is disjoint
|
||||
// from a type if all of its constraints are.
|
||||
(Type::TypeVar(typevar), other) | (other, Type::TypeVar(typevar)) => {
|
||||
match typevar.bound_or_constraints(db) {
|
||||
(Type::TypeVar(bound_typevar), other) | (other, Type::TypeVar(bound_typevar)) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
bound.is_disjoint_from_impl(db, other, visitor)
|
||||
|
@ -2331,14 +2340,16 @@ impl<'db> Type<'db> {
|
|||
// the bound is a final singleton class, since it can still be specialized to `Never`.
|
||||
// A constrained typevar is a singleton if all of its constraints are singletons. (Note
|
||||
// that you cannot specialize a constrained typevar to a subtype of a constraint.)
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(_)) => false,
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|constraint| constraint.is_singleton(db)),
|
||||
},
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(_)) => false,
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|constraint| constraint.is_singleton(db)),
|
||||
}
|
||||
}
|
||||
|
||||
// We eagerly transform `SubclassOf` to `ClassLiteral` for final types, so `SubclassOf` is never a singleton.
|
||||
Type::SubclassOf(..) => false,
|
||||
|
@ -2466,14 +2477,16 @@ impl<'db> Type<'db> {
|
|||
// `Never`. A constrained typevar is single-valued if all of its constraints are
|
||||
// single-valued. (Note that you cannot specialize a constrained typevar to a subtype
|
||||
// of a constraint.)
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(_)) => false,
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|constraint| constraint.is_single_valued(db)),
|
||||
},
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(_)) => false,
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|constraint| constraint.is_single_valued(db)),
|
||||
}
|
||||
}
|
||||
|
||||
Type::SubclassOf(..) => {
|
||||
// TODO: Same comment as above for `is_singleton`
|
||||
|
@ -2735,16 +2748,18 @@ impl<'db> Type<'db> {
|
|||
KnownClass::Object.to_instance(db).instance_member(db, name)
|
||||
}
|
||||
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => KnownClass::Object.to_instance(db).instance_member(db, name),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
bound.instance_member(db, name)
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => KnownClass::Object.to_instance(db).instance_member(db, name),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
bound.instance_member(db, name)
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||
.map_with_boundness_and_qualifiers(db, |constraint| {
|
||||
constraint.instance_member(db, name)
|
||||
}),
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
|
||||
.map_with_boundness_and_qualifiers(db, |constraint| {
|
||||
constraint.instance_member(db, name)
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
Type::IntLiteral(_) => KnownClass::Int.to_instance(db).instance_member(db, name),
|
||||
Type::BooleanLiteral(_) | Type::TypeIs(_) => {
|
||||
|
@ -3606,15 +3621,17 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
},
|
||||
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => Truthiness::Ambiguous,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
bound.try_bool_impl(db, allow_short_circuit)?
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => Truthiness::Ambiguous,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
bound.try_bool_impl(db, allow_short_circuit)?
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
try_union(constraints)?
|
||||
}
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
try_union(constraints)?
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Type::NominalInstance(instance) => match instance.class.known(db) {
|
||||
Some(known_class) => known_class.bool(),
|
||||
|
@ -3710,14 +3727,18 @@ impl<'db> Type<'db> {
|
|||
.into()
|
||||
}
|
||||
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => CallableBinding::not_callable(self).into(),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.bindings(db),
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => Bindings::from_union(
|
||||
self,
|
||||
constraints.elements(db).iter().map(|ty| ty.bindings(db)),
|
||||
),
|
||||
},
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => CallableBinding::not_callable(self).into(),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.bindings(db),
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
Bindings::from_union(
|
||||
self,
|
||||
constraints.elements(db).iter().map(|ty| ty.bindings(db)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type::BoundMethod(bound_method) => {
|
||||
let signature = bound_method.function(db).signature(db);
|
||||
|
@ -5210,7 +5231,8 @@ impl<'db> Type<'db> {
|
|||
// If there is no bound or constraints on a typevar `T`, `T: object` implicitly, which
|
||||
// has no instance type. Otherwise, synthesize a typevar with bound or constraints
|
||||
// mapped through `to_instance`.
|
||||
Type::TypeVar(typevar) => {
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
let typevar = bound_typevar.typevar(db);
|
||||
let bound_or_constraints = match typevar.bound_or_constraints(db)? {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound) => {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound.to_instance(db)?)
|
||||
|
@ -5221,15 +5243,18 @@ impl<'db> Type<'db> {
|
|||
)
|
||||
}
|
||||
};
|
||||
Some(Type::TypeVar(TypeVarInstance::new(
|
||||
Some(Type::TypeVar(BoundTypeVarInstance::new(
|
||||
db,
|
||||
Name::new(format!("{}'instance", typevar.name(db))),
|
||||
None,
|
||||
None,
|
||||
Some(bound_or_constraints),
|
||||
typevar.variance(db),
|
||||
None,
|
||||
typevar.kind(db),
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
Name::new(format!("{}'instance", typevar.name(db))),
|
||||
None,
|
||||
Some(bound_or_constraints),
|
||||
typevar.variance(db),
|
||||
None,
|
||||
typevar.kind(db),
|
||||
),
|
||||
bound_typevar.binding_context(db),
|
||||
)))
|
||||
}
|
||||
Type::Intersection(_) => Some(todo_type!("Type::Intersection.to_instance")),
|
||||
|
@ -5268,13 +5293,13 @@ impl<'db> Type<'db> {
|
|||
/// expression, it names the type `Type::NominalInstance(builtins.int)`, that is, all objects whose
|
||||
/// `__class__` is `int`.
|
||||
///
|
||||
/// The `scope_id` and `legacy_typevar_binding_context` arguments must always come from the file we are currently inferring, so
|
||||
/// The `scope_id` and `typevar_binding_context` arguments must always come from the file we are currently inferring, so
|
||||
/// as to avoid cross-module AST dependency.
|
||||
pub(crate) fn in_type_expression(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
scope_id: ScopeId<'db>,
|
||||
legacy_typevar_binding_context: Option<Definition<'db>>,
|
||||
typevar_binding_context: Option<Definition<'db>>,
|
||||
) -> Result<Type<'db>, InvalidTypeExpressionError<'db>> {
|
||||
match self {
|
||||
// Special cases for `float` and `complex`
|
||||
|
@ -5342,7 +5367,20 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::KnownInstance(known_instance) => match known_instance {
|
||||
KnownInstanceType::TypeAliasType(alias) => Ok(alias.value_type(db)),
|
||||
KnownInstanceType::TypeVar(typevar) => Ok(Type::TypeVar(*typevar)),
|
||||
KnownInstanceType::TypeVar(typevar) => {
|
||||
let module = parsed_module(db, scope_id.file(db)).load(db);
|
||||
let index = semantic_index(db, scope_id.file(db));
|
||||
Ok(bind_typevar(
|
||||
db,
|
||||
&module,
|
||||
index,
|
||||
scope_id.file_scope_id(db),
|
||||
typevar_binding_context,
|
||||
*typevar,
|
||||
)
|
||||
.map(Type::TypeVar)
|
||||
.unwrap_or(*self))
|
||||
}
|
||||
KnownInstanceType::Deprecated(_) => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::Deprecated],
|
||||
fallback_type: Type::unknown(),
|
||||
|
@ -5407,22 +5445,21 @@ impl<'db> Type<'db> {
|
|||
db,
|
||||
ast::name::Name::new_static("Self"),
|
||||
Some(class_definition),
|
||||
None,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(instance)),
|
||||
TypeVarVariance::Invariant,
|
||||
None,
|
||||
TypeVarKind::Implicit,
|
||||
);
|
||||
let typevar = bind_legacy_typevar(
|
||||
Ok(bind_typevar(
|
||||
db,
|
||||
&module,
|
||||
index,
|
||||
scope_id.file_scope_id(db),
|
||||
legacy_typevar_binding_context,
|
||||
typevar_binding_context,
|
||||
typevar,
|
||||
)
|
||||
.unwrap_or(typevar);
|
||||
Ok(Type::TypeVar(typevar))
|
||||
.map(Type::TypeVar)
|
||||
.unwrap_or(*self))
|
||||
}
|
||||
SpecialFormType::TypeAlias => Ok(Type::Dynamic(DynamicType::TodoTypeAlias)),
|
||||
SpecialFormType::TypedDict => Err(InvalidTypeExpressionError {
|
||||
|
@ -5497,7 +5534,7 @@ impl<'db> Type<'db> {
|
|||
let mut builder = UnionBuilder::new(db);
|
||||
let mut invalid_expressions = smallvec::SmallVec::default();
|
||||
for element in union.elements(db) {
|
||||
match element.in_type_expression(db, scope_id, legacy_typevar_binding_context) {
|
||||
match element.in_type_expression(db, scope_id, typevar_binding_context) {
|
||||
Ok(type_expr) => builder = builder.add(type_expr),
|
||||
Err(InvalidTypeExpressionError {
|
||||
fallback_type,
|
||||
|
@ -5621,15 +5658,17 @@ impl<'db> Type<'db> {
|
|||
Type::Tuple(tuple) => tuple
|
||||
.to_subclass_of(db)
|
||||
.unwrap_or_else(SubclassOfType::subclass_of_unknown),
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => KnownClass::Type.to_instance(db),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.to_meta_type(db),
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
// TODO: If we add a proper `OneOf` connector, we should use that here instead
|
||||
// of union. (Using a union here doesn't break anything, but it is imprecise.)
|
||||
constraints.map(db, |constraint| constraint.to_meta_type(db))
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => KnownClass::Type.to_instance(db),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.to_meta_type(db),
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
// TODO: If we add a proper `OneOf` connector, we should use that here instead
|
||||
// of union. (Using a union here doesn't break anything, but it is imprecise.)
|
||||
constraints.map(db, |constraint| constraint.to_meta_type(db))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Type::ClassLiteral(class) => class.metaclass(db),
|
||||
Type::GenericAlias(alias) => ClassType::from(*alias).metaclass(db),
|
||||
|
@ -5709,16 +5748,22 @@ impl<'db> Type<'db> {
|
|||
type_mapping: &TypeMapping<'a, 'db>,
|
||||
) -> Type<'db> {
|
||||
match self {
|
||||
Type::TypeVar(typevar) => match type_mapping {
|
||||
Type::TypeVar(bound_typevar) => match type_mapping {
|
||||
TypeMapping::Specialization(specialization) => {
|
||||
specialization.get(db, typevar).unwrap_or(self)
|
||||
specialization.get(db, bound_typevar).unwrap_or(self)
|
||||
}
|
||||
TypeMapping::PartialSpecialization(partial) => {
|
||||
partial.get(db, typevar).unwrap_or(self)
|
||||
partial.get(db, bound_typevar).unwrap_or(self)
|
||||
}
|
||||
TypeMapping::PromoteLiterals | TypeMapping::BindLegacyTypevars(_) => self,
|
||||
}
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::PromoteLiterals => self,
|
||||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||
Type::TypeVar(typevar.with_binding_context(db, *binding_context))
|
||||
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5841,80 +5886,94 @@ impl<'db> Type<'db> {
|
|||
pub(crate) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self {
|
||||
Type::TypeVar(typevar) => {
|
||||
if typevar.is_legacy(db) || typevar.is_implicit(db) {
|
||||
typevars.insert(typevar);
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
if matches!(
|
||||
bound_typevar.typevar(db).kind(db),
|
||||
TypeVarKind::Legacy | TypeVarKind::Implicit
|
||||
) && binding_context.is_none_or(|binding_context| {
|
||||
bound_typevar.binding_context(db) == BindingContext::Definition(binding_context)
|
||||
}) {
|
||||
typevars.insert(bound_typevar);
|
||||
}
|
||||
}
|
||||
|
||||
Type::FunctionLiteral(function) => function.find_legacy_typevars(db, typevars),
|
||||
Type::FunctionLiteral(function) => {
|
||||
function.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::BoundMethod(method) => {
|
||||
method.self_instance(db).find_legacy_typevars(db, typevars);
|
||||
method.function(db).find_legacy_typevars(db, typevars);
|
||||
method
|
||||
.self_instance(db)
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
method
|
||||
.function(db)
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::MethodWrapper(
|
||||
MethodWrapperKind::FunctionTypeDunderGet(function)
|
||||
| MethodWrapperKind::FunctionTypeDunderCall(function),
|
||||
) => {
|
||||
function.find_legacy_typevars(db, typevars);
|
||||
function.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::MethodWrapper(
|
||||
MethodWrapperKind::PropertyDunderGet(property)
|
||||
| MethodWrapperKind::PropertyDunderSet(property),
|
||||
) => {
|
||||
property.find_legacy_typevars(db, typevars);
|
||||
property.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::Callable(callable) => {
|
||||
callable.find_legacy_typevars(db, typevars);
|
||||
callable.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::PropertyInstance(property) => {
|
||||
property.find_legacy_typevars(db, typevars);
|
||||
property.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::Union(union) => {
|
||||
for element in union.iter(db) {
|
||||
element.find_legacy_typevars(db, typevars);
|
||||
element.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
Type::Intersection(intersection) => {
|
||||
for positive in intersection.positive(db) {
|
||||
positive.find_legacy_typevars(db, typevars);
|
||||
positive.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
for negative in intersection.negative(db) {
|
||||
negative.find_legacy_typevars(db, typevars);
|
||||
negative.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
Type::Tuple(tuple) => {
|
||||
tuple.find_legacy_typevars(db, typevars);
|
||||
tuple.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::GenericAlias(alias) => {
|
||||
alias.find_legacy_typevars(db, typevars);
|
||||
alias.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::NominalInstance(instance) => {
|
||||
instance.find_legacy_typevars(db, typevars);
|
||||
instance.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::ProtocolInstance(instance) => {
|
||||
instance.find_legacy_typevars(db, typevars);
|
||||
instance.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::SubclassOf(subclass_of) => {
|
||||
subclass_of.find_legacy_typevars(db, typevars);
|
||||
subclass_of.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::TypeIs(type_is) => {
|
||||
type_is.return_type(db).find_legacy_typevars(db, typevars);
|
||||
type_is
|
||||
.return_type(db)
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
Type::Dynamic(_)
|
||||
|
@ -6044,7 +6103,7 @@ impl<'db> Type<'db> {
|
|||
| Self::BoundSuper(_)
|
||||
| Self::Tuple(_) => self.to_meta_type(db).definition(db),
|
||||
|
||||
Self::TypeVar(var) => Some(TypeDefinition::TypeVar(var.definition(db)?)),
|
||||
Self::TypeVar(bound_typevar) => Some(TypeDefinition::TypeVar(bound_typevar.typevar(db).definition(db)?)),
|
||||
|
||||
Self::ProtocolInstance(protocol) => match protocol.inner {
|
||||
Protocol::FromClass(class) => Some(TypeDefinition::Class(class.definition(db))),
|
||||
|
@ -6168,7 +6227,7 @@ pub enum TypeMapping<'a, 'db> {
|
|||
PromoteLiterals,
|
||||
/// Binds a legacy typevar with the generic context (class, function, type alias) that it is
|
||||
/// being used in.
|
||||
BindLegacyTypevars(Definition<'db>),
|
||||
BindLegacyTypevars(BindingContext<'db>),
|
||||
}
|
||||
|
||||
fn walk_type_mapping<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
@ -6359,7 +6418,9 @@ impl<'db> KnownInstanceType<'db> {
|
|||
// This is a legacy `TypeVar` _outside_ of any generic class or function, so we render
|
||||
// it as an instance of `typing.TypeVar`. Inside of a generic class or function, we'll
|
||||
// have a `Type::TypeVar(_)`, which is rendered as the typevar's name.
|
||||
KnownInstanceType::TypeVar(_) => f.write_str("typing.TypeVar"),
|
||||
KnownInstanceType::TypeVar(typevar) => {
|
||||
write!(f, "typing.TypeVar({})", typevar.display(self.db))
|
||||
}
|
||||
KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"),
|
||||
KnownInstanceType::Field(field) => {
|
||||
f.write_str("dataclasses.Field[")?;
|
||||
|
@ -6738,13 +6799,38 @@ pub enum TypeVarKind {
|
|||
Implicit,
|
||||
}
|
||||
|
||||
/// Data regarding a single type variable.
|
||||
/// A type variable that has not been bound to a generic context yet.
|
||||
///
|
||||
/// This is referenced by `KnownInstanceType::TypeVar` (to represent the singleton type of the
|
||||
/// runtime `typing.TypeVar` object itself), and by `Type::TypeVar` to represent the type that this
|
||||
/// typevar represents as an annotation: that is, an unknown set of objects, constrained by the
|
||||
/// upper-bound/constraints on this type var, defaulting to the default type of this type var when
|
||||
/// not otherwise bound to a type.
|
||||
/// This is usually not the type that you want; if you are working with a typevar, in a generic
|
||||
/// context, which might be specialized to a concrete type, you want [`BoundTypeVarInstance`]. This
|
||||
/// type holds information that does not depend on which generic context the typevar is used in.
|
||||
///
|
||||
/// For a legacy typevar:
|
||||
///
|
||||
/// ```py
|
||||
/// T = TypeVar("T") # [1]
|
||||
/// def generic_function(t: T) -> T: ... # [2]
|
||||
/// ```
|
||||
///
|
||||
/// we will create a `TypeVarInstance` for the typevar `T` when it is instantiated. The type of `T`
|
||||
/// at `[1]` will be a `KnownInstanceType::TypeVar` wrapping this `TypeVarInstance`. The typevar is
|
||||
/// not yet bound to any generic context at this point.
|
||||
///
|
||||
/// The typevar is used in `generic_function`, which binds it to a new generic context. We will
|
||||
/// create a [`BoundTypeVarInstance`] for this new binding of the typevar. The type of `T` at `[2]`
|
||||
/// will be a `Type::TypeVar` wrapping this `BoundTypeVarInstance`.
|
||||
///
|
||||
/// For a PEP 695 typevar:
|
||||
///
|
||||
/// ```py
|
||||
/// def generic_function[T](t: T) -> T: ...
|
||||
/// # ╰─────╰─────────── [2]
|
||||
/// # ╰─────────────────────── [1]
|
||||
/// ```
|
||||
///
|
||||
/// the typevar is defined and immediately bound to a single generic context. Just like in the
|
||||
/// legacy case, we will create a `TypeVarInstance` and [`BoundTypeVarInstance`], and the type of
|
||||
/// `T` at `[1]` and `[2]` will be that `TypeVarInstance` and `BoundTypeVarInstance`, respectively.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the type var instance's salsa-assigned id and not on its values.
|
||||
|
@ -6759,35 +6845,6 @@ pub struct TypeVarInstance<'db> {
|
|||
/// The type var's definition (None if synthesized)
|
||||
pub definition: Option<Definition<'db>>,
|
||||
|
||||
/// The definition of the generic class, function, or type alias that binds this typevar. This
|
||||
/// is `None` for a legacy typevar outside of a context that can bind it.
|
||||
///
|
||||
/// For a legacy typevar, the binding context might be missing:
|
||||
///
|
||||
/// ```py
|
||||
/// T = TypeVar("T") # [1]
|
||||
/// def generic_function(t: T) -> T: ... # [2]
|
||||
/// ```
|
||||
///
|
||||
/// Here, we will create two `TypeVarInstance`s for the typevar `T`. Both will have `[1]` as
|
||||
/// their [`definition`][Self::definition]. The first represents the variable when it is first
|
||||
/// created, and not yet used, so it's `binding_context` will be `None`. The second represents
|
||||
/// when the typevar is used in `generic_function`, and its `binding_context` will be `[2]`
|
||||
/// (that is, the definition of `generic_function`).
|
||||
///
|
||||
/// For a PEP 695 typevar, there will always be a binding context, since you can only define
|
||||
/// one as part of creating the generic context that uses it:
|
||||
///
|
||||
/// ```py
|
||||
/// def generic_function[T](t: T) -> T: ...
|
||||
/// ```
|
||||
///
|
||||
/// Here, we will create a single `TypeVarInstance`. Its [`definition`][Self::definition] will
|
||||
/// be the `T` in `[T]` (i.e., the definition of the typevar in the syntactic construct that
|
||||
/// creates the generic context that uses it). Its `binding_context` will be the definition of
|
||||
/// `generic_function`.
|
||||
binding_context: Option<Definition<'db>>,
|
||||
|
||||
/// The upper bound or constraint on the type of this TypeVar
|
||||
bound_or_constraints: Option<TypeVarBoundOrConstraints<'db>>,
|
||||
|
||||
|
@ -6805,13 +6862,13 @@ impl get_size2::GetSize for TypeVarInstance<'_> {}
|
|||
|
||||
fn walk_type_var_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
db: &'db dyn Db,
|
||||
type_var: TypeVarInstance<'db>,
|
||||
typevar: TypeVarInstance<'db>,
|
||||
visitor: &mut V,
|
||||
) {
|
||||
if let Some(bounds) = type_var.bound_or_constraints(db) {
|
||||
if let Some(bounds) = typevar.bound_or_constraints(db) {
|
||||
walk_type_var_bounds(db, bounds, visitor);
|
||||
}
|
||||
if let Some(default_type) = type_var.default_ty(db) {
|
||||
if let Some(default_type) = typevar.default_ty(db) {
|
||||
visitor.visit_type(db, default_type);
|
||||
}
|
||||
}
|
||||
|
@ -6821,23 +6878,8 @@ impl<'db> TypeVarInstance<'db> {
|
|||
self,
|
||||
db: &'db dyn Db,
|
||||
binding_context: Definition<'db>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
Some(binding_context),
|
||||
self.bound_or_constraints(db),
|
||||
self.variance(db),
|
||||
self.default_ty(db).map(|ty| {
|
||||
ty.apply_type_mapping(db, &TypeMapping::BindLegacyTypevars(binding_context))
|
||||
}),
|
||||
self.kind(db),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn is_legacy(self, db: &'db dyn Db) -> bool {
|
||||
matches!(self.kind(db), TypeVarKind::Legacy)
|
||||
) -> BoundTypeVarInstance<'db> {
|
||||
BoundTypeVarInstance::new(db, self, BindingContext::Definition(binding_context))
|
||||
}
|
||||
|
||||
pub(crate) fn is_implicit(self, db: &'db dyn Db) -> bool {
|
||||
|
@ -6869,7 +6911,6 @@ impl<'db> TypeVarInstance<'db> {
|
|||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.binding_context(db),
|
||||
self.bound_or_constraints(db)
|
||||
.map(|b| b.normalized_impl(db, visitor)),
|
||||
self.variance(db),
|
||||
|
@ -6883,7 +6924,6 @@ impl<'db> TypeVarInstance<'db> {
|
|||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.binding_context(db),
|
||||
self.bound_or_constraints(db)
|
||||
.map(|b| b.materialize(db, variance)),
|
||||
self.variance(db),
|
||||
|
@ -6893,6 +6933,96 @@ impl<'db> TypeVarInstance<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Where a type variable is bound and usable.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
|
||||
pub enum BindingContext<'db> {
|
||||
/// The definition of the generic class, function, or type alias that binds this typevar.
|
||||
Definition(Definition<'db>),
|
||||
/// The typevar is synthesized internally, and is not associated with a particular definition
|
||||
/// in the source, but is still bound and eligible for specialization inference.
|
||||
Synthetic,
|
||||
}
|
||||
|
||||
impl<'db> BindingContext<'db> {
|
||||
fn name(self, db: &'db dyn Db) -> Option<String> {
|
||||
match self {
|
||||
BindingContext::Definition(definition) => definition.name(db),
|
||||
BindingContext::Synthetic => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type variable that has been bound to a generic context, and which can be specialized to a
|
||||
/// concrete type.
|
||||
#[salsa::interned(debug)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct BoundTypeVarInstance<'db> {
|
||||
pub typevar: TypeVarInstance<'db>,
|
||||
binding_context: BindingContext<'db>,
|
||||
}
|
||||
|
||||
// The Salsa heap is tracked separately.
|
||||
impl get_size2::GetSize for BoundTypeVarInstance<'_> {}
|
||||
|
||||
fn walk_bound_type_var_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
db: &'db dyn Db,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
visitor: &mut V,
|
||||
) {
|
||||
visitor.visit_type_var_type(db, bound_typevar.typevar(db));
|
||||
}
|
||||
|
||||
impl<'db> BoundTypeVarInstance<'db> {
|
||||
/// Returns the default value of this typevar, recursively applying its binding context to any
|
||||
/// other typevars that appear in the default.
|
||||
///
|
||||
/// For instance, in
|
||||
///
|
||||
/// ```py
|
||||
/// T = TypeVar("T")
|
||||
/// U = TypeVar("U", default=T)
|
||||
///
|
||||
/// # revealed: typing.TypeVar[U = typing.TypeVar[T]]
|
||||
/// reveal_type(U)
|
||||
///
|
||||
/// # revealed: typing.Generic[T, U = T@C]
|
||||
/// class C(reveal_type(Generic[T, U])): ...
|
||||
/// ```
|
||||
///
|
||||
/// In the first case, the use of `U` is unbound, and so we have a [`TypeVarInstance`], and its
|
||||
/// default value (`T`) is also unbound.
|
||||
///
|
||||
/// By using `U` in the generic class, it becomes bound, and so we have a
|
||||
/// `BoundTypeVarInstance`. As part of binding `U` we must also bind its default value
|
||||
/// (resulting in `T@C`).
|
||||
pub(crate) fn default_ty(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
let binding_context = self.binding_context(db);
|
||||
self.typevar(db)
|
||||
.default_ty(db)
|
||||
.map(|ty| ty.apply_type_mapping(db, &TypeMapping::BindLegacyTypevars(binding_context)))
|
||||
}
|
||||
|
||||
pub(crate) fn normalized_impl(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
visitor: &mut TypeTransformer<'db>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.typevar(db).normalized_impl(db, visitor),
|
||||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
|
||||
fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.typevar(db).materialize(db, variance),
|
||||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
|
||||
pub enum TypeVarVariance {
|
||||
Invariant,
|
||||
|
@ -8109,9 +8239,11 @@ impl<'db> CallableType<'db> {
|
|||
fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
self.signatures(db).find_legacy_typevars(db, typevars);
|
||||
self.signatures(db)
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
/// Check whether this callable type has the given relation to another callable type.
|
||||
|
|
|
@ -973,11 +973,11 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
let mut positive_to_remove = SmallVec::<[usize; 1]>::new();
|
||||
|
||||
for (typevar_index, ty) in self.positive.iter().enumerate() {
|
||||
let Type::TypeVar(typevar) = ty else {
|
||||
let Type::TypeVar(bound_typevar) = ty else {
|
||||
continue;
|
||||
};
|
||||
let Some(TypeVarBoundOrConstraints::Constraints(constraints)) =
|
||||
typevar.bound_or_constraints(db)
|
||||
bound_typevar.typevar(db).bound_or_constraints(db)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
@ -2857,7 +2857,7 @@ impl<'db> BindingError<'db> {
|
|||
return;
|
||||
};
|
||||
|
||||
let typevar = error.typevar();
|
||||
let typevar = error.bound_typevar().typevar(context.db());
|
||||
let argument_type = error.argument_type();
|
||||
let argument_ty_display = argument_type.display(context.db());
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::sync::{LazyLock, Mutex};
|
|||
|
||||
use super::TypeVarVariance;
|
||||
use super::{
|
||||
IntersectionBuilder, MemberLookupPolicy, Mro, MroError, MroIterator, SpecialFormType,
|
||||
SubclassOfType, Truthiness, Type, TypeQualifiers,
|
||||
BoundTypeVarInstance, IntersectionBuilder, MemberLookupPolicy, Mro, MroError, MroIterator,
|
||||
SpecialFormType, SubclassOfType, Truthiness, Type, TypeQualifiers,
|
||||
class_base::ClassBase,
|
||||
function::{FunctionDecorators, FunctionType},
|
||||
infer_expression_type, infer_unpack_types,
|
||||
|
@ -270,11 +270,13 @@ impl<'db> GenericAlias<'db> {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
// A tuple's specialization will include all of its element types, so we don't need to also
|
||||
// look in `self.tuple`.
|
||||
self.specialization(db).find_legacy_typevars(db, typevars);
|
||||
self.specialization(db)
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
pub(super) fn is_typed_dict(self, db: &'db dyn Db) -> bool {
|
||||
|
@ -415,11 +417,12 @@ impl<'db> ClassType<'db> {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self {
|
||||
Self::NonGeneric(_) => {}
|
||||
Self::Generic(generic) => generic.find_legacy_typevars(db, typevars),
|
||||
Self::Generic(generic) => generic.find_legacy_typevars(db, binding_context, typevars),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1220,11 +1223,13 @@ impl<'db> ClassLiteral<'db> {
|
|||
#[salsa::tracked(cycle_fn=pep695_generic_context_cycle_recover, cycle_initial=pep695_generic_context_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn pep695_generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
|
||||
let scope = self.body_scope(db);
|
||||
let parsed = parsed_module(db, scope.file(db)).load(db);
|
||||
let file = scope.file(db);
|
||||
let parsed = parsed_module(db, file).load(db);
|
||||
let class_def_node = scope.node(db).expect_class(&parsed);
|
||||
class_def_node.type_params.as_ref().map(|type_params| {
|
||||
let index = semantic_index(db, scope.file(db));
|
||||
GenericContext::from_type_params(db, index, type_params)
|
||||
let index = semantic_index(db, file);
|
||||
let definition = index.expect_single_definition(class_def_node);
|
||||
GenericContext::from_type_params(db, index, definition, type_params)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1339,21 +1344,7 @@ impl<'db> ClassLiteral<'db> {
|
|||
class_stmt
|
||||
.bases()
|
||||
.iter()
|
||||
.map(
|
||||
|base_node| match definition_expression_type(db, class_definition, base_node) {
|
||||
Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(generic_context)) => {
|
||||
Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(
|
||||
generic_context.with_binding_context(db, class_definition),
|
||||
))
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(
|
||||
generic_context,
|
||||
)) => Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(
|
||||
generic_context.with_binding_context(db, class_definition),
|
||||
)),
|
||||
ty => ty,
|
||||
},
|
||||
)
|
||||
.map(|base_node| definition_expression_type(db, class_definition, base_node))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -4454,15 +4445,11 @@ impl KnownClass {
|
|||
};
|
||||
|
||||
let containing_assignment = index.expect_single_definition(target);
|
||||
// A freshly created legacy TypeVar does not have a binding context until it is
|
||||
// used in a base class list, function parameter list, or type alias.
|
||||
let binding_context = None;
|
||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::TypeVar(
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
&target.id,
|
||||
Some(containing_assignment),
|
||||
binding_context,
|
||||
bound_or_constraint,
|
||||
variance,
|
||||
*default,
|
||||
|
|
|
@ -7,17 +7,17 @@ use ruff_python_ast::str::{Quote, TripleQuotes};
|
|||
use ruff_python_literal::escape::AsciiEscape;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::Db;
|
||||
use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
|
||||
use crate::types::function::{FunctionType, OverloadLiteral};
|
||||
use crate::types::generics::{GenericContext, Specialization};
|
||||
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::TupleSpec;
|
||||
use crate::types::{
|
||||
CallableType, IntersectionType, KnownClass, MethodWrapperKind, Protocol, StringLiteralType,
|
||||
SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance, UnionType,
|
||||
WrapperDescriptorKind,
|
||||
BoundTypeVarInstance, CallableType, IntersectionType, KnownClass, MethodWrapperKind, Protocol,
|
||||
StringLiteralType, SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||
UnionType, WrapperDescriptorKind,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
impl<'db> Type<'db> {
|
||||
pub fn display(&self, db: &'db dyn Db) -> DisplayType<'_> {
|
||||
|
@ -208,11 +208,9 @@ impl Display for DisplayRepresentation<'_> {
|
|||
)
|
||||
}
|
||||
Type::Tuple(specialization) => specialization.tuple(self.db).display(self.db).fmt(f),
|
||||
Type::TypeVar(typevar) => {
|
||||
f.write_str(typevar.name(self.db))?;
|
||||
if let Some(binding_context) = typevar
|
||||
.binding_context(self.db)
|
||||
.and_then(|def| def.name(self.db))
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
f.write_str(bound_typevar.typevar(self.db).name(self.db))?;
|
||||
if let Some(binding_context) = bound_typevar.binding_context(self.db).name(self.db)
|
||||
{
|
||||
write!(f, "@{binding_context}")?;
|
||||
}
|
||||
|
@ -413,6 +411,83 @@ impl Display for DisplayGenericAlias<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'db> TypeVarInstance<'db> {
|
||||
pub(crate) fn display(self, db: &'db dyn Db) -> DisplayTypeVarInstance<'db> {
|
||||
DisplayTypeVarInstance { typevar: self, db }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DisplayTypeVarInstance<'db> {
|
||||
typevar: TypeVarInstance<'db>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl Display for DisplayTypeVarInstance<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
display_quoted_string(self.typevar.name(self.db)).fmt(f)?;
|
||||
match self.typevar.bound_or_constraints(self.db) {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
write!(f, ", bound={}", bound.display(self.db))?;
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
for constraint in constraints.iter(self.db) {
|
||||
write!(f, ", {}", constraint.display(self.db))?;
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if let Some(default_type) = self.typevar.default_ty(self.db) {
|
||||
write!(f, ", default={}", default_type.display(self.db))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> BoundTypeVarInstance<'db> {
|
||||
pub(crate) fn display(self, db: &'db dyn Db) -> DisplayBoundTypeVarInstance<'db> {
|
||||
DisplayBoundTypeVarInstance {
|
||||
bound_typevar: self,
|
||||
db,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DisplayBoundTypeVarInstance<'db> {
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl Display for DisplayBoundTypeVarInstance<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
// This looks very much like DisplayTypeVarInstance::fmt, but note that we have typevar
|
||||
// default values in a subtly different way: if the default value contains other typevars,
|
||||
// here those must be bound as well, whereas in DisplayTypeVarInstance they should not. See
|
||||
// BoundTypeVarInstance::default_ty for more details.
|
||||
let typevar = self.bound_typevar.typevar(self.db);
|
||||
f.write_str(typevar.name(self.db))?;
|
||||
match typevar.bound_or_constraints(self.db) {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
write!(f, ": {}", bound.display(self.db))?;
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
f.write_str(": (")?;
|
||||
for (idx, constraint) in constraints.iter(self.db).enumerate() {
|
||||
if idx > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
constraint.display(self.db).fmt(f)?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if let Some(default_type) = self.bound_typevar.default_ty(self.db) {
|
||||
write!(f, " = {}", default_type.display(self.db))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> GenericContext<'db> {
|
||||
pub fn display(&'db self, db: &'db dyn Db) -> DisplayGenericContext<'db> {
|
||||
DisplayGenericContext {
|
||||
|
@ -452,7 +527,7 @@ impl Display for DisplayGenericContext<'_> {
|
|||
|
||||
let non_implicit_variables: Vec<_> = variables
|
||||
.iter()
|
||||
.filter(|var| !var.is_implicit(self.db))
|
||||
.filter(|bound_typevar| !bound_typevar.typevar(self.db).is_implicit(self.db))
|
||||
.collect();
|
||||
|
||||
if non_implicit_variables.is_empty() {
|
||||
|
@ -460,30 +535,11 @@ impl Display for DisplayGenericContext<'_> {
|
|||
}
|
||||
|
||||
f.write_char('[')?;
|
||||
for (idx, var) in non_implicit_variables.iter().enumerate() {
|
||||
for (idx, bound_typevar) in non_implicit_variables.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
f.write_str(var.name(self.db))?;
|
||||
match var.bound_or_constraints(self.db) {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
write!(f, ": {}", bound.display(self.db))?;
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
f.write_str(": (")?;
|
||||
for (idx, constraint) in constraints.iter(self.db).enumerate() {
|
||||
if idx > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
constraint.display(self.db).fmt(f)?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if let Some(default_type) = var.default_ty(self.db) {
|
||||
write!(f, " = {}", default_type.display(self.db))?;
|
||||
}
|
||||
bound_typevar.display(self.db).fmt(f)?;
|
||||
}
|
||||
f.write_char(']')
|
||||
}
|
||||
|
@ -497,7 +553,6 @@ impl<'db> Specialization<'db> {
|
|||
tuple_specialization: TupleSpecialization,
|
||||
) -> DisplaySpecialization<'db> {
|
||||
DisplaySpecialization {
|
||||
typevars: self.generic_context(db).variables(db),
|
||||
types: self.types(db),
|
||||
db,
|
||||
tuple_specialization,
|
||||
|
@ -506,7 +561,6 @@ impl<'db> Specialization<'db> {
|
|||
}
|
||||
|
||||
pub struct DisplaySpecialization<'db> {
|
||||
typevars: &'db FxOrderSet<TypeVarInstance<'db>>,
|
||||
types: &'db [Type<'db>],
|
||||
db: &'db dyn Db,
|
||||
tuple_specialization: TupleSpecialization,
|
||||
|
@ -515,7 +569,7 @@ pub struct DisplaySpecialization<'db> {
|
|||
impl Display for DisplaySpecialization<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_char('[')?;
|
||||
for (idx, (_, ty)) in self.typevars.iter().zip(self.types).enumerate() {
|
||||
for (idx, ty) in self.types.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
|
@ -1016,20 +1070,22 @@ impl Display for DisplayTypeArray<'_, '_> {
|
|||
|
||||
impl<'db> StringLiteralType<'db> {
|
||||
fn display(&'db self, db: &'db dyn Db) -> DisplayStringLiteralType<'db> {
|
||||
DisplayStringLiteralType { db, ty: self }
|
||||
display_quoted_string(self.value(db))
|
||||
}
|
||||
}
|
||||
|
||||
fn display_quoted_string(string: &str) -> DisplayStringLiteralType<'_> {
|
||||
DisplayStringLiteralType { string }
|
||||
}
|
||||
|
||||
struct DisplayStringLiteralType<'db> {
|
||||
ty: &'db StringLiteralType<'db>,
|
||||
db: &'db dyn Db,
|
||||
string: &'db str,
|
||||
}
|
||||
|
||||
impl Display for DisplayStringLiteralType<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let value = self.ty.value(self.db);
|
||||
f.write_char('"')?;
|
||||
for ch in value.chars() {
|
||||
for ch in self.string.chars() {
|
||||
match ch {
|
||||
// `escape_debug` will escape even single quotes, which is not necessary for our
|
||||
// use case as we are already using double quotes to wrap the string.
|
||||
|
|
|
@ -76,9 +76,9 @@ use crate::types::narrow::ClassInfoConstraintFunction;
|
|||
use crate::types::signatures::{CallableSignature, Signature};
|
||||
use crate::types::visitor::any_over_type;
|
||||
use crate::types::{
|
||||
BoundMethodType, CallableType, ClassBase, ClassLiteral, ClassType, DeprecatedInstance,
|
||||
DynamicType, KnownClass, Truthiness, Type, TypeMapping, TypeRelation, TypeTransformer,
|
||||
TypeVarInstance, UnionBuilder, all_members, walk_type_mapping,
|
||||
BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType,
|
||||
DeprecatedInstance, DynamicType, KnownClass, Truthiness, Type, TypeMapping, TypeRelation,
|
||||
TypeTransformer, UnionBuilder, all_members, walk_type_mapping,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
@ -338,7 +338,7 @@ impl<'db> OverloadLiteral<'db> {
|
|||
let definition = self.definition(db);
|
||||
let generic_context = function_stmt_node.type_params.as_ref().map(|type_params| {
|
||||
let index = semantic_index(db, scope.file(db));
|
||||
GenericContext::from_type_params(db, index, type_params)
|
||||
GenericContext::from_type_params(db, index, definition, type_params)
|
||||
});
|
||||
|
||||
let index = semantic_index(db, scope.file(db));
|
||||
|
@ -575,6 +575,30 @@ impl<'db> FunctionLiteral<'db> {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Typed externally-visible signature of the last overload or implementation of this function.
|
||||
///
|
||||
/// ## Warning
|
||||
///
|
||||
/// This uses the semantic index to find the definition of the function. This means that if the
|
||||
/// calling query is not in the same file as this function is defined in, then this will create
|
||||
/// a cross-module dependency directly on the full AST which will lead to cache
|
||||
/// over-invalidation.
|
||||
fn last_definition_signature<'a>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
type_mappings: &'a [TypeMapping<'a, 'db>],
|
||||
) -> Signature<'db>
|
||||
where
|
||||
'db: 'a,
|
||||
{
|
||||
let inherited_generic_context = self.inherited_generic_context(db);
|
||||
type_mappings.iter().fold(
|
||||
self.last_definition(db)
|
||||
.signature(db, inherited_generic_context),
|
||||
|ty, mapping| ty.apply_type_mapping(db, mapping),
|
||||
)
|
||||
}
|
||||
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
|
||||
let context = self
|
||||
.inherited_generic_context(db)
|
||||
|
@ -795,6 +819,26 @@ impl<'db> FunctionType<'db> {
|
|||
self.literal(db).signature(db, self.type_mappings(db))
|
||||
}
|
||||
|
||||
/// Typed externally-visible signature of the last overload or implementation of this function.
|
||||
///
|
||||
/// ## Why is this a salsa query?
|
||||
///
|
||||
/// This is a salsa query to short-circuit the invalidation
|
||||
/// when the function's AST node changes.
|
||||
///
|
||||
/// Were this not a salsa query, then the calling query
|
||||
/// would depend on the function's AST and rerun for every change in that file.
|
||||
#[salsa::tracked(
|
||||
returns(ref),
|
||||
cycle_fn=last_definition_signature_cycle_recover,
|
||||
cycle_initial=last_definition_signature_cycle_initial,
|
||||
heap_size=ruff_memory_usage::heap_size,
|
||||
)]
|
||||
pub(crate) fn last_definition_signature(self, db: &'db dyn Db) -> Signature<'db> {
|
||||
self.literal(db)
|
||||
.last_definition_signature(db, self.type_mappings(db))
|
||||
}
|
||||
|
||||
/// Convert the `FunctionType` into a [`CallableType`].
|
||||
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> CallableType<'db> {
|
||||
CallableType::new(db, self.signature(db), false)
|
||||
|
@ -861,11 +905,12 @@ impl<'db> FunctionType<'db> {
|
|||
pub(crate) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
let signatures = self.signature(db);
|
||||
for signature in &signatures.overloads {
|
||||
signature.find_legacy_typevars(db, typevars);
|
||||
signature.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1000,6 +1045,22 @@ fn signature_cycle_initial<'db>(
|
|||
CallableSignature::single(Signature::bottom(db))
|
||||
}
|
||||
|
||||
fn last_definition_signature_cycle_recover<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_value: &Signature<'db>,
|
||||
_count: u32,
|
||||
_function: FunctionType<'db>,
|
||||
) -> salsa::CycleRecoveryAction<Signature<'db>> {
|
||||
salsa::CycleRecoveryAction::Iterate
|
||||
}
|
||||
|
||||
fn last_definition_signature_cycle_initial<'db>(
|
||||
db: &'db dyn Db,
|
||||
_function: FunctionType<'db>,
|
||||
) -> Signature<'db> {
|
||||
Signature::bottom(db)
|
||||
}
|
||||
|
||||
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
|
||||
/// have special behavior.
|
||||
#[derive(
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||
use ruff_db::parsed::ParsedModuleRef;
|
||||
use ruff_python_ast as ast;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::semantic_index::SemanticIndex;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind};
|
||||
use crate::semantic_index::{SemanticIndex, semantic_index};
|
||||
use crate::types::class::ClassType;
|
||||
use crate::types::class_base::ClassBase;
|
||||
use crate::types::infer::infer_definition_types;
|
||||
|
@ -14,8 +14,9 @@ use crate::types::instance::{NominalInstanceType, Protocol, ProtocolInstanceType
|
|||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
use crate::types::{
|
||||
KnownInstanceType, Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarBoundOrConstraints,
|
||||
TypeVarInstance, TypeVarVariance, UnionType, binding_type, declaration_type,
|
||||
BoundTypeVarInstance, KnownInstanceType, Type, TypeMapping, TypeRelation, TypeTransformer,
|
||||
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarVariance, UnionType, binding_type,
|
||||
declaration_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
@ -31,64 +32,51 @@ fn enclosing_generic_contexts<'db>(
|
|||
.ancestor_scopes(scope)
|
||||
.filter_map(|(_, ancestor_scope)| match ancestor_scope.node() {
|
||||
NodeWithScopeKind::Class(class) => {
|
||||
binding_type(db, index.expect_single_definition(class.node(module)))
|
||||
let definition = index.expect_single_definition(class.node(module));
|
||||
binding_type(db, definition)
|
||||
.into_class_literal()?
|
||||
.generic_context(db)
|
||||
}
|
||||
NodeWithScopeKind::Function(function) => {
|
||||
infer_definition_types(db, index.expect_single_definition(function.node(module)))
|
||||
let definition = index.expect_single_definition(function.node(module));
|
||||
infer_definition_types(db, definition)
|
||||
.undecorated_type()
|
||||
.expect("function should have undecorated type")
|
||||
.into_function_literal()?
|
||||
.signature(db)
|
||||
.iter()
|
||||
.last()
|
||||
.expect("function should have at least one overload")
|
||||
.last_definition_signature(db)
|
||||
.generic_context
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the legacy typevars that have been bound in the given scope or any enclosing scope.
|
||||
fn bound_legacy_typevars<'db>(
|
||||
db: &'db dyn Db,
|
||||
module: &ParsedModuleRef,
|
||||
index: &'db SemanticIndex<'db>,
|
||||
scope: FileScopeId,
|
||||
) -> impl Iterator<Item = TypeVarInstance<'db>> {
|
||||
enclosing_generic_contexts(db, module, index, scope)
|
||||
.flat_map(|generic_context| generic_context.variables(db).iter().copied())
|
||||
.filter(|typevar| typevar.is_legacy(db))
|
||||
}
|
||||
|
||||
/// Binds an unbound legacy typevar.
|
||||
/// Binds an unbound typevar.
|
||||
///
|
||||
/// When a legacy typevar is first created, we will have a [`TypeVarInstance`] which does not have
|
||||
/// an associated binding context. When the typevar is used in a generic class or function, we
|
||||
/// "bind" it, adding the [`Definition`] of the generic class or function as its "binding context".
|
||||
/// When a typevar is first created, we will have a [`TypeVarInstance`] which does not have an
|
||||
/// associated binding context. When the typevar is used in a generic class or function, we "bind"
|
||||
/// it, adding the [`Definition`] of the generic class or function as its "binding context".
|
||||
///
|
||||
/// When an expression resolves to a legacy typevar, our inferred type will refer to the unbound
|
||||
/// When an expression resolves to a typevar, our inferred type will refer to the unbound
|
||||
/// [`TypeVarInstance`] from when the typevar was first created. This function walks the scopes
|
||||
/// that enclosing the expression, looking for the innermost binding context that binds the
|
||||
/// typevar.
|
||||
///
|
||||
/// If no enclosing scope has already bound the typevar, we might be in a syntactic position that
|
||||
/// is about to bind it (indicated by a non-`None` `legacy_typevar_binding_context`), in which case
|
||||
/// we bind the typevar with that new binding context.
|
||||
pub(crate) fn bind_legacy_typevar<'db>(
|
||||
/// is about to bind it (indicated by a non-`None` `typevar_binding_context`), in which case we
|
||||
/// bind the typevar with that new binding context.
|
||||
pub(crate) fn bind_typevar<'db>(
|
||||
db: &'db dyn Db,
|
||||
module: &ParsedModuleRef,
|
||||
index: &SemanticIndex<'db>,
|
||||
containing_scope: FileScopeId,
|
||||
legacy_typevar_binding_context: Option<Definition<'db>>,
|
||||
typevar_binding_context: Option<Definition<'db>>,
|
||||
typevar: TypeVarInstance<'db>,
|
||||
) -> Option<TypeVarInstance<'db>> {
|
||||
) -> Option<BoundTypeVarInstance<'db>> {
|
||||
enclosing_generic_contexts(db, module, index, containing_scope)
|
||||
.find_map(|enclosing_context| enclosing_context.binds_legacy_typevar(db, typevar))
|
||||
.find_map(|enclosing_context| enclosing_context.binds_typevar(db, typevar))
|
||||
.or_else(|| {
|
||||
legacy_typevar_binding_context.map(|legacy_typevar_binding_context| {
|
||||
typevar.with_binding_context(db, legacy_typevar_binding_context)
|
||||
typevar_binding_context.map(|typevar_binding_context| {
|
||||
typevar.with_binding_context(db, typevar_binding_context)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -105,7 +93,7 @@ pub(crate) fn bind_legacy_typevar<'db>(
|
|||
#[derive(PartialOrd, Ord)]
|
||||
pub struct GenericContext<'db> {
|
||||
#[returns(ref)]
|
||||
pub(crate) variables: FxOrderSet<TypeVarInstance<'db>>,
|
||||
pub(crate) variables: FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
}
|
||||
|
||||
pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
@ -113,8 +101,8 @@ pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?S
|
|||
context: GenericContext<'db>,
|
||||
visitor: &mut V,
|
||||
) {
|
||||
for typevar in context.variables(db) {
|
||||
visitor.visit_type_var_type(db, *typevar);
|
||||
for bound_typevar in context.variables(db) {
|
||||
visitor.visit_bound_type_var_type(db, *bound_typevar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,11 +114,14 @@ impl<'db> GenericContext<'db> {
|
|||
pub(crate) fn from_type_params(
|
||||
db: &'db dyn Db,
|
||||
index: &'db SemanticIndex<'db>,
|
||||
binding_context: Definition<'db>,
|
||||
type_params_node: &ast::TypeParams,
|
||||
) -> Self {
|
||||
let variables: FxOrderSet<_> = type_params_node
|
||||
.iter()
|
||||
.filter_map(|type_param| Self::variable_from_type_param(db, index, type_param))
|
||||
.filter_map(|type_param| {
|
||||
Self::variable_from_type_param(db, index, binding_context, type_param)
|
||||
})
|
||||
.collect();
|
||||
Self::new(db, variables)
|
||||
}
|
||||
|
@ -138,8 +129,9 @@ impl<'db> GenericContext<'db> {
|
|||
fn variable_from_type_param(
|
||||
db: &'db dyn Db,
|
||||
index: &'db SemanticIndex<'db>,
|
||||
binding_context: Definition<'db>,
|
||||
type_param_node: &ast::TypeParam,
|
||||
) -> Option<TypeVarInstance<'db>> {
|
||||
) -> Option<BoundTypeVarInstance<'db>> {
|
||||
match type_param_node {
|
||||
ast::TypeParam::TypeVar(node) => {
|
||||
let definition = index.expect_single_definition(node);
|
||||
|
@ -148,7 +140,7 @@ impl<'db> GenericContext<'db> {
|
|||
else {
|
||||
return None;
|
||||
};
|
||||
Some(typevar)
|
||||
Some(typevar.with_binding_context(db, binding_context))
|
||||
}
|
||||
// TODO: Support these!
|
||||
ast::TypeParam::ParamSpec(_) => None,
|
||||
|
@ -168,23 +160,14 @@ impl<'db> GenericContext<'db> {
|
|||
let mut variables = FxOrderSet::default();
|
||||
for param in parameters {
|
||||
if let Some(ty) = param.annotated_type() {
|
||||
ty.find_legacy_typevars(db, &mut variables);
|
||||
ty.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
}
|
||||
if let Some(ty) = param.default_type() {
|
||||
ty.find_legacy_typevars(db, &mut variables);
|
||||
ty.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
}
|
||||
}
|
||||
if let Some(ty) = return_type {
|
||||
ty.find_legacy_typevars(db, &mut variables);
|
||||
}
|
||||
|
||||
// Then remove any that were bound in enclosing scopes.
|
||||
let file = definition.file(db);
|
||||
let module = parsed_module(db, file).load(db);
|
||||
let index = semantic_index(db, file);
|
||||
let containing_scope = definition.file_scope(db);
|
||||
for typevar in bound_legacy_typevars(db, &module, index, containing_scope) {
|
||||
variables.remove(&typevar);
|
||||
ty.find_legacy_typevars(db, Some(definition), &mut variables);
|
||||
}
|
||||
|
||||
if variables.is_empty() {
|
||||
|
@ -201,7 +184,7 @@ impl<'db> GenericContext<'db> {
|
|||
) -> Option<Self> {
|
||||
let mut variables = FxOrderSet::default();
|
||||
for base in bases {
|
||||
base.find_legacy_typevars(db, &mut variables);
|
||||
base.find_legacy_typevars(db, None, &mut variables);
|
||||
}
|
||||
if variables.is_empty() {
|
||||
return None;
|
||||
|
@ -209,19 +192,6 @@ impl<'db> GenericContext<'db> {
|
|||
Some(Self::new(db, variables))
|
||||
}
|
||||
|
||||
pub(crate) fn with_binding_context(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
binding_context: Definition<'db>,
|
||||
) -> Self {
|
||||
let variables: FxOrderSet<_> = self
|
||||
.variables(db)
|
||||
.iter()
|
||||
.map(|typevar| typevar.with_binding_context(db, binding_context))
|
||||
.collect();
|
||||
Self::new(db, variables)
|
||||
}
|
||||
|
||||
pub(crate) fn len(self, db: &'db dyn Db) -> usize {
|
||||
self.variables(db).len()
|
||||
}
|
||||
|
@ -235,7 +205,11 @@ impl<'db> GenericContext<'db> {
|
|||
Signature::new(parameters, None)
|
||||
}
|
||||
|
||||
fn parameter_from_typevar(db: &'db dyn Db, typevar: TypeVarInstance<'db>) -> Parameter<'db> {
|
||||
fn parameter_from_typevar(
|
||||
db: &'db dyn Db,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
) -> Parameter<'db> {
|
||||
let typevar = bound_typevar.typevar(db);
|
||||
let mut parameter = Parameter::positional_only(Some(typevar.name(db).clone()));
|
||||
match typevar.bound_or_constraints(db) {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
|
@ -250,7 +224,7 @@ impl<'db> GenericContext<'db> {
|
|||
}
|
||||
None => {}
|
||||
}
|
||||
if let Some(default_ty) = typevar.default_ty(db) {
|
||||
if let Some(default_ty) = bound_typevar.default_ty(db) {
|
||||
parameter = parameter.with_default_type(default_ty);
|
||||
}
|
||||
parameter
|
||||
|
@ -288,19 +262,14 @@ impl<'db> GenericContext<'db> {
|
|||
self.variables(db).is_subset(other.variables(db))
|
||||
}
|
||||
|
||||
pub(crate) fn binds_legacy_typevar(
|
||||
pub(crate) fn binds_typevar(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevar: TypeVarInstance<'db>,
|
||||
) -> Option<TypeVarInstance<'db>> {
|
||||
assert!(typevar.is_legacy(db) || typevar.is_implicit(db));
|
||||
let typevar_def = typevar.definition(db);
|
||||
) -> Option<BoundTypeVarInstance<'db>> {
|
||||
self.variables(db)
|
||||
.iter()
|
||||
.find(|self_typevar| {
|
||||
(self_typevar.is_legacy(db) || self_typevar.is_implicit(db))
|
||||
&& self_typevar.definition(db) == typevar_def
|
||||
})
|
||||
.find(|self_bound_typevar| self_bound_typevar.typevar(db) == typevar)
|
||||
.copied()
|
||||
}
|
||||
|
||||
|
@ -380,7 +349,7 @@ impl<'db> GenericContext<'db> {
|
|||
let variables: FxOrderSet<_> = self
|
||||
.variables(db)
|
||||
.iter()
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.map(|bound_typevar| bound_typevar.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
Self::new(db, variables)
|
||||
}
|
||||
|
@ -454,11 +423,15 @@ impl<'db> Specialization<'db> {
|
|||
|
||||
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
|
||||
/// mapping.
|
||||
pub(crate) fn get(self, db: &'db dyn Db, typevar: TypeVarInstance<'db>) -> Option<Type<'db>> {
|
||||
pub(crate) fn get(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
) -> Option<Type<'db>> {
|
||||
let index = self
|
||||
.generic_context(db)
|
||||
.variables(db)
|
||||
.get_index_of(&typevar)?;
|
||||
.get_index_of(&bound_typevar)?;
|
||||
self.types(db).get(index).copied()
|
||||
}
|
||||
|
||||
|
@ -556,8 +529,8 @@ impl<'db> Specialization<'db> {
|
|||
.variables(db)
|
||||
.into_iter()
|
||||
.zip(self.types(db))
|
||||
.map(|(typevar, vartype)| {
|
||||
let variance = match typevar.variance(db) {
|
||||
.map(|(bound_typevar, vartype)| {
|
||||
let variance = match bound_typevar.typevar(db).variance(db) {
|
||||
TypeVarVariance::Invariant => TypeVarVariance::Invariant,
|
||||
TypeVarVariance::Covariant => variance,
|
||||
TypeVarVariance::Contravariant => variance.flip(),
|
||||
|
@ -589,7 +562,7 @@ impl<'db> Specialization<'db> {
|
|||
return self_tuple.has_relation_to(db, other_tuple, relation);
|
||||
}
|
||||
|
||||
for ((typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
||||
.zip(self.types(db))
|
||||
.zip(other.types(db))
|
||||
{
|
||||
|
@ -606,7 +579,7 @@ impl<'db> Specialization<'db> {
|
|||
// - contravariant: verify that other_type <: self_type
|
||||
// - invariant: verify that self_type <: other_type AND other_type <: self_type
|
||||
// - bivariant: skip, can't make subtyping/assignability false
|
||||
let compatible = match typevar.variance(db) {
|
||||
let compatible = match bound_typevar.typevar(db).variance(db) {
|
||||
TypeVarVariance::Invariant => match relation {
|
||||
TypeRelation::Subtyping => self_type.is_equivalent_to(db, *other_type),
|
||||
TypeRelation::Assignability => {
|
||||
|
@ -634,7 +607,7 @@ impl<'db> Specialization<'db> {
|
|||
return false;
|
||||
}
|
||||
|
||||
for ((typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
||||
.zip(self.types(db))
|
||||
.zip(other.types(db))
|
||||
{
|
||||
|
@ -644,7 +617,7 @@ impl<'db> Specialization<'db> {
|
|||
// - contravariant: verify that other_type == self_type
|
||||
// - invariant: verify that self_type == other_type
|
||||
// - bivariant: skip, can't make equivalence false
|
||||
let compatible = match typevar.variance(db) {
|
||||
let compatible = match bound_typevar.typevar(db).variance(db) {
|
||||
TypeVarVariance::Invariant
|
||||
| TypeVarVariance::Covariant
|
||||
| TypeVarVariance::Contravariant => self_type.is_equivalent_to(db, *other_type),
|
||||
|
@ -661,10 +634,11 @@ impl<'db> Specialization<'db> {
|
|||
pub(crate) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
for ty in self.types(db) {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -693,8 +667,15 @@ pub(super) fn walk_partial_specialization<'db, V: super::visitor::TypeVisitor<'d
|
|||
impl<'db> PartialSpecialization<'_, 'db> {
|
||||
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
|
||||
/// mapping.
|
||||
pub(crate) fn get(&self, db: &'db dyn Db, typevar: TypeVarInstance<'db>) -> Option<Type<'db>> {
|
||||
let index = self.generic_context.variables(db).get_index_of(&typevar)?;
|
||||
pub(crate) fn get(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
) -> Option<Type<'db>> {
|
||||
let index = self
|
||||
.generic_context
|
||||
.variables(db)
|
||||
.get_index_of(&bound_typevar)?;
|
||||
self.types.get(index).copied()
|
||||
}
|
||||
|
||||
|
@ -728,7 +709,7 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
|||
/// specialization of a generic function.
|
||||
pub(crate) struct SpecializationBuilder<'db> {
|
||||
db: &'db dyn Db,
|
||||
types: FxHashMap<TypeVarInstance<'db>, Type<'db>>,
|
||||
types: FxHashMap<BoundTypeVarInstance<'db>, Type<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> SpecializationBuilder<'db> {
|
||||
|
@ -754,9 +735,9 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
Specialization::new(self.db, generic_context, types, None)
|
||||
}
|
||||
|
||||
fn add_type_mapping(&mut self, typevar: TypeVarInstance<'db>, ty: Type<'db>) {
|
||||
fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
|
||||
self.types
|
||||
.entry(typevar)
|
||||
.entry(bound_typevar)
|
||||
.and_modify(|existing| {
|
||||
*existing = UnionType::from_elements(self.db, [*existing, ty]);
|
||||
})
|
||||
|
@ -803,14 +784,14 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
// and add a mapping between that typevar and the actual type. (Note that we've
|
||||
// already handled above the case where the actual is assignable to a _non-typevar_
|
||||
// union element.)
|
||||
let mut typevars = formal.iter(self.db).filter_map(|ty| match ty {
|
||||
Type::TypeVar(typevar) => Some(*typevar),
|
||||
let mut bound_typevars = formal.iter(self.db).filter_map(|ty| match ty {
|
||||
Type::TypeVar(bound_typevar) => Some(*bound_typevar),
|
||||
_ => None,
|
||||
});
|
||||
let typevar = typevars.next();
|
||||
let additional_typevars = typevars.next();
|
||||
if let (Some(typevar), None) = (typevar, additional_typevars) {
|
||||
self.add_type_mapping(typevar, actual);
|
||||
let bound_typevar = bound_typevars.next();
|
||||
let additional_bound_typevars = bound_typevars.next();
|
||||
if let (Some(bound_typevar), None) = (bound_typevar, additional_bound_typevars) {
|
||||
self.add_type_mapping(bound_typevar, actual);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -824,31 +805,31 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
(Type::TypeVar(typevar), ty) | (ty, Type::TypeVar(typevar)) => {
|
||||
match typevar.bound_or_constraints(self.db) {
|
||||
(Type::TypeVar(bound_typevar), ty) | (ty, Type::TypeVar(bound_typevar)) => {
|
||||
match bound_typevar.typevar(self.db).bound_or_constraints(self.db) {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
if !ty.is_assignable_to(self.db, bound) {
|
||||
return Err(SpecializationError::MismatchedBound {
|
||||
typevar,
|
||||
bound_typevar,
|
||||
argument: ty,
|
||||
});
|
||||
}
|
||||
self.add_type_mapping(typevar, ty);
|
||||
self.add_type_mapping(bound_typevar, ty);
|
||||
}
|
||||
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => {
|
||||
for constraint in constraints.iter(self.db) {
|
||||
if ty.is_assignable_to(self.db, *constraint) {
|
||||
self.add_type_mapping(typevar, *constraint);
|
||||
self.add_type_mapping(bound_typevar, *constraint);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
return Err(SpecializationError::MismatchedConstraint {
|
||||
typevar,
|
||||
bound_typevar,
|
||||
argument: ty,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.add_type_mapping(typevar, ty);
|
||||
self.add_type_mapping(bound_typevar, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -920,20 +901,20 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum SpecializationError<'db> {
|
||||
MismatchedBound {
|
||||
typevar: TypeVarInstance<'db>,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
argument: Type<'db>,
|
||||
},
|
||||
MismatchedConstraint {
|
||||
typevar: TypeVarInstance<'db>,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
argument: Type<'db>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'db> SpecializationError<'db> {
|
||||
pub(crate) fn typevar(&self) -> TypeVarInstance<'db> {
|
||||
pub(crate) fn bound_typevar(&self) -> BoundTypeVarInstance<'db> {
|
||||
match self {
|
||||
Self::MismatchedBound { typevar, .. } => *typevar,
|
||||
Self::MismatchedConstraint { typevar, .. } => *typevar,
|
||||
Self::MismatchedBound { bound_typevar, .. } => *bound_typevar,
|
||||
Self::MismatchedConstraint { bound_typevar, .. } => *bound_typevar,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ use crate::types::enums::is_enum_class;
|
|||
use crate::types::function::{
|
||||
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
||||
};
|
||||
use crate::types::generics::{GenericContext, bind_legacy_typevar};
|
||||
use crate::types::generics::{GenericContext, bind_typevar};
|
||||
use crate::types::mro::MroErrorKind;
|
||||
use crate::types::signatures::{CallableSignature, Signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
|
@ -816,8 +816,8 @@ pub(super) struct TypeInferenceBuilder<'db, 'ast> {
|
|||
/// [`check_overloaded_functions`]: TypeInferenceBuilder::check_overloaded_functions
|
||||
called_functions: FxHashSet<FunctionType<'db>>,
|
||||
|
||||
/// Whether we are in a context that binds unbound legacy typevars.
|
||||
legacy_typevar_binding_context: Option<Definition<'db>>,
|
||||
/// Whether we are in a context that binds unbound typevars.
|
||||
typevar_binding_context: Option<Definition<'db>>,
|
||||
|
||||
/// The deferred state of inferring types of certain expressions within the region.
|
||||
///
|
||||
|
@ -866,7 +866,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
expressions: FxHashMap::default(),
|
||||
bindings: VecMap::default(),
|
||||
declarations: VecMap::default(),
|
||||
legacy_typevar_binding_context: None,
|
||||
typevar_binding_context: None,
|
||||
deferred: VecSet::default(),
|
||||
undecorated_type: None,
|
||||
cycle_fallback: false,
|
||||
|
@ -2242,12 +2242,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.as_deref()
|
||||
.expect("class type params scope without type params");
|
||||
|
||||
let binding_context = self.index.expect_single_definition(class);
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(binding_context);
|
||||
|
||||
self.infer_type_parameters(type_params);
|
||||
|
||||
if let Some(arguments) = class.arguments.as_deref() {
|
||||
// Note: We do not install a new `legacy_typevar_binding_context`; since this class has
|
||||
// PEP 695 typevars, it should not also bind any legacy typevars via inheriting from
|
||||
// `typing.Generic` or `typing.Protocol`.
|
||||
let mut call_arguments =
|
||||
CallArguments::from_arguments(self.db(), arguments, |argument, splatted_value| {
|
||||
let ty = self.infer_expression(splatted_value);
|
||||
|
@ -2257,6 +2258,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
|
||||
}
|
||||
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
|
||||
fn infer_class_body(&mut self, class: &ast::StmtClassDef) {
|
||||
|
@ -2269,15 +2272,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.as_deref()
|
||||
.expect("function type params scope without type params");
|
||||
|
||||
// Note: We do not install a new `legacy_typevar_binding_context`; since this function has
|
||||
// PEP 695 typevars, it should not also bind any legacy typevars by referencing them in its
|
||||
// parameter or return type annotations.
|
||||
let binding_context = self.index.expect_single_definition(function);
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(binding_context);
|
||||
self.infer_return_type_annotation(
|
||||
function.returns.as_deref(),
|
||||
DeferredExpressionState::None,
|
||||
);
|
||||
self.infer_type_parameters(type_params);
|
||||
self.infer_parameters(&function.parameters);
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
|
||||
fn infer_type_alias_type_params(&mut self, type_alias: &ast::StmtTypeAlias) {
|
||||
|
@ -2286,7 +2290,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.as_ref()
|
||||
.expect("type alias type params scope without type params");
|
||||
|
||||
let binding_context = self.index.expect_single_definition(type_alias);
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(binding_context);
|
||||
self.infer_type_parameters(type_params);
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
|
||||
fn infer_type_alias(&mut self, type_alias: &ast::StmtTypeAlias) {
|
||||
|
@ -2634,14 +2642,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
if self.defer_annotations() {
|
||||
self.deferred.insert(definition);
|
||||
} else {
|
||||
let previous_legacy_typevar_binding_context =
|
||||
self.legacy_typevar_binding_context.replace(definition);
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(definition);
|
||||
self.infer_return_type_annotation(
|
||||
returns.as_deref(),
|
||||
DeferredExpressionState::None,
|
||||
);
|
||||
self.infer_parameters(parameters);
|
||||
self.legacy_typevar_binding_context = previous_legacy_typevar_binding_context;
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3054,12 +3062,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
if self.in_stub() || class_node.bases().iter().any(contains_string_literal) {
|
||||
self.deferred.insert(definition);
|
||||
} else {
|
||||
let previous_legacy_typevar_binding_context =
|
||||
self.legacy_typevar_binding_context.replace(definition);
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(definition);
|
||||
for base in class_node.bases() {
|
||||
self.infer_expression(base);
|
||||
}
|
||||
self.legacy_typevar_binding_context = previous_legacy_typevar_binding_context;
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3069,23 +3077,21 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
definition: Definition<'db>,
|
||||
function: &ast::StmtFunctionDef,
|
||||
) {
|
||||
let previous_legacy_typevar_binding_context =
|
||||
self.legacy_typevar_binding_context.replace(definition);
|
||||
let previous_typevar_binding_context = self.typevar_binding_context.replace(definition);
|
||||
self.infer_return_type_annotation(
|
||||
function.returns.as_deref(),
|
||||
DeferredExpressionState::Deferred,
|
||||
);
|
||||
self.infer_parameters(function.parameters.as_ref());
|
||||
self.legacy_typevar_binding_context = previous_legacy_typevar_binding_context;
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
|
||||
fn infer_class_deferred(&mut self, definition: Definition<'db>, class: &ast::StmtClassDef) {
|
||||
let previous_legacy_typevar_binding_context =
|
||||
self.legacy_typevar_binding_context.replace(definition);
|
||||
let previous_typevar_binding_context = self.typevar_binding_context.replace(definition);
|
||||
for base in class.bases() {
|
||||
self.infer_expression(base);
|
||||
}
|
||||
self.legacy_typevar_binding_context = previous_legacy_typevar_binding_context;
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
|
||||
fn infer_type_alias_definition(
|
||||
|
@ -3394,27 +3400,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
default,
|
||||
} = node;
|
||||
|
||||
// Find the binding context for the PEP 695 typevars defined in this scope. The typevar
|
||||
// scope should have a child containing the class, function, or type alias definition. Find
|
||||
// that scope and use its definition as the binding context.
|
||||
let typevar_scope = definition.file_scope(self.db());
|
||||
let child_scopes = self.index.child_scopes(typevar_scope);
|
||||
let binding_context = child_scopes
|
||||
.filter_map(|(_, binding_scope)| match binding_scope.node() {
|
||||
NodeWithScopeKind::Class(class) => {
|
||||
Some(DefinitionNodeKey::from(class.node(self.context.module())))
|
||||
}
|
||||
NodeWithScopeKind::Function(function) => Some(DefinitionNodeKey::from(
|
||||
function.node(self.context.module()),
|
||||
)),
|
||||
NodeWithScopeKind::TypeAlias(alias) => {
|
||||
Some(DefinitionNodeKey::from(alias.node(self.context.module())))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|key| self.index.expect_single_definition(key))
|
||||
.next();
|
||||
|
||||
let bound_or_constraint = match bound.as_deref() {
|
||||
Some(expr @ ast::Expr::Tuple(ast::ExprTuple { elts, .. })) => {
|
||||
if elts.len() < 2 {
|
||||
|
@ -3459,7 +3444,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.db(),
|
||||
&name.id,
|
||||
Some(definition),
|
||||
binding_context,
|
||||
bound_or_constraint,
|
||||
TypeVarVariance::Invariant, // TODO: infer this
|
||||
default_ty,
|
||||
|
@ -6520,43 +6504,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.infer_place_load(PlaceExprRef::from(&expr), ast::ExprRef::Name(name_node));
|
||||
|
||||
resolved
|
||||
.map_type(|ty| match ty {
|
||||
// If the expression resolves to a legacy typevar, we will have the TypeVarInstance
|
||||
// that was created when the typevar was created, which will not have an associated
|
||||
// binding context. If this expression appears inside of a generic context that
|
||||
// binds that typevar, we need to update the TypeVarInstance to include that
|
||||
// binding context. To do that, we walk the enclosing scopes, looking for the
|
||||
// nearest generic context that binds the typevar.
|
||||
//
|
||||
// If the legacy typevar is still unbound after that search, and we are in a
|
||||
// context that binds unbound legacy typevars (i.e., the signature of a generic
|
||||
// function), bind it with that context.
|
||||
Type::TypeVar(typevar) if typevar.is_legacy(self.db()) => bind_legacy_typevar(
|
||||
self.db(),
|
||||
self.context.module(),
|
||||
self.index,
|
||||
self.scope().file_scope_id(self.db()),
|
||||
self.legacy_typevar_binding_context,
|
||||
typevar,
|
||||
)
|
||||
.map(Type::TypeVar)
|
||||
.unwrap_or(ty),
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar))
|
||||
if typevar.is_legacy(self.db()) =>
|
||||
{
|
||||
bind_legacy_typevar(
|
||||
self.db(),
|
||||
self.context.module(),
|
||||
self.index,
|
||||
self.scope().file_scope_id(self.db()),
|
||||
self.legacy_typevar_binding_context,
|
||||
typevar,
|
||||
)
|
||||
.map(|typevar| Type::KnownInstance(KnownInstanceType::TypeVar(typevar)))
|
||||
.unwrap_or(ty)
|
||||
}
|
||||
_ => ty,
|
||||
})
|
||||
// Not found in the module's explicitly declared global symbols?
|
||||
// Check the "implicit globals" such as `__doc__`, `__file__`, `__name__`, etc.
|
||||
// These are looked up as attributes on `types.ModuleType`.
|
||||
|
@ -9069,7 +9016,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let typevars: Result<FxOrderSet<_>, GenericContextError> = typevars
|
||||
.iter()
|
||||
.map(|typevar| match typevar {
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Ok(*typevar),
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => bind_typevar(
|
||||
self.db(),
|
||||
self.module(),
|
||||
self.index,
|
||||
self.scope().file_scope_id(self.db()),
|
||||
self.typevar_binding_context,
|
||||
*typevar,
|
||||
)
|
||||
.ok_or(GenericContextError::InvalidArgument),
|
||||
Type::Dynamic(DynamicType::TodoUnpack) => Err(GenericContextError::NotYetSupported),
|
||||
Type::NominalInstance(NominalInstanceType { class, .. })
|
||||
if matches!(
|
||||
|
@ -9167,7 +9122,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
undecorated_type: _,
|
||||
|
||||
// builder only state
|
||||
legacy_typevar_binding_context: _,
|
||||
typevar_binding_context: _,
|
||||
deferred_state: _,
|
||||
called_functions: _,
|
||||
index: _,
|
||||
|
@ -9228,7 +9183,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
undecorated_type,
|
||||
|
||||
// builder only state
|
||||
legacy_typevar_binding_context: _,
|
||||
typevar_binding_context: _,
|
||||
deferred_state: _,
|
||||
called_functions: _,
|
||||
index: _,
|
||||
|
@ -9298,7 +9253,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
undecorated_type: _,
|
||||
|
||||
// Builder only state
|
||||
legacy_typevar_binding_context: _,
|
||||
typevar_binding_context: _,
|
||||
deferred_state: _,
|
||||
called_functions: _,
|
||||
index: _,
|
||||
|
@ -9407,7 +9362,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
.in_type_expression(
|
||||
self.db(),
|
||||
self.scope(),
|
||||
self.legacy_typevar_binding_context,
|
||||
self.typevar_binding_context,
|
||||
)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
|
@ -9627,11 +9582,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
ast::Expr::Name(name) => match name.ctx {
|
||||
ast::ExprContext::Load => self
|
||||
.infer_name_expression(name)
|
||||
.in_type_expression(
|
||||
self.db(),
|
||||
self.scope(),
|
||||
self.legacy_typevar_binding_context,
|
||||
)
|
||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&self.context,
|
||||
|
@ -9648,11 +9599,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
ast::Expr::Attribute(attribute_expression) => match attribute_expression.ctx {
|
||||
ast::ExprContext::Load => self
|
||||
.infer_attribute_expression(attribute_expression)
|
||||
.in_type_expression(
|
||||
self.db(),
|
||||
self.scope(),
|
||||
self.legacy_typevar_binding_context,
|
||||
)
|
||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||
.unwrap_or_else(|error| {
|
||||
error.into_fallback_type(
|
||||
&self.context,
|
||||
|
@ -10341,7 +10288,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
.in_type_expression(
|
||||
self.db(),
|
||||
self.scope(),
|
||||
self.legacy_typevar_binding_context,
|
||||
self.typevar_binding_context,
|
||||
)
|
||||
.unwrap_or(Type::unknown())
|
||||
}
|
||||
|
@ -11555,11 +11502,12 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let check_typevar = |var: &'static str,
|
||||
display: &'static str,
|
||||
upper_bound: Option<&'static str>,
|
||||
constraints: Option<&[&'static str]>,
|
||||
default: Option<&'static str>| {
|
||||
let var_ty = get_symbol(&db, "src/a.py", &["f"], var).expect_type();
|
||||
assert_eq!(var_ty.display(&db).to_string(), "typing.TypeVar");
|
||||
assert_eq!(var_ty.display(&db).to_string(), display);
|
||||
|
||||
let expected_name_ty = format!(r#"Literal["{var}"]"#);
|
||||
let name_ty = var_ty.member(&db, "__name__").place.expect_type();
|
||||
|
@ -11593,14 +11541,32 @@ mod tests {
|
|||
);
|
||||
};
|
||||
|
||||
check_typevar("T", None, None, None);
|
||||
check_typevar("U", Some("A"), None, None);
|
||||
check_typevar("V", None, Some(&["A", "B"]), None);
|
||||
check_typevar("W", None, None, Some("A"));
|
||||
check_typevar("X", Some("A"), None, Some("A1"));
|
||||
check_typevar("T", "typing.TypeVar(\"T\")", None, None, None);
|
||||
check_typevar("U", "typing.TypeVar(\"U\", bound=A)", Some("A"), None, None);
|
||||
check_typevar(
|
||||
"V",
|
||||
"typing.TypeVar(\"V\", A, B)",
|
||||
None,
|
||||
Some(&["A", "B"]),
|
||||
None,
|
||||
);
|
||||
check_typevar(
|
||||
"W",
|
||||
"typing.TypeVar(\"W\", default=A)",
|
||||
None,
|
||||
None,
|
||||
Some("A"),
|
||||
);
|
||||
check_typevar(
|
||||
"X",
|
||||
"typing.TypeVar(\"X\", bound=A, default=A1)",
|
||||
Some("A"),
|
||||
None,
|
||||
Some("A1"),
|
||||
);
|
||||
|
||||
// a typevar with less than two constraints is treated as unconstrained
|
||||
check_typevar("Y", None, None, None);
|
||||
check_typevar("Y", "typing.TypeVar(\"Y\")", None, None, None);
|
||||
}
|
||||
|
||||
/// Test that a symbol known to be unbound in a scope does not still trigger cycle-causing
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use super::protocol_class::ProtocolInterface;
|
||||
use super::{ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
|
||||
use super::{BoundTypeVarInstance, ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
|
||||
use crate::place::PlaceAndQualifiers;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::cyclic::PairVisitor;
|
||||
use crate::types::enums::is_single_member_enum;
|
||||
use crate::types::protocol_class::walk_protocol_interface;
|
||||
use crate::types::tuple::TupleType;
|
||||
use crate::types::{
|
||||
DynamicType, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance, TypedDictType,
|
||||
};
|
||||
use crate::types::{DynamicType, TypeMapping, TypeRelation, TypeTransformer, TypedDictType};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||
|
@ -161,9 +160,11 @@ impl<'db> NominalInstanceType<'db> {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
self.class.find_legacy_typevars(db, typevars);
|
||||
self.class
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,14 +343,15 @@ impl<'db> ProtocolInstanceType<'db> {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self.inner {
|
||||
Protocol::FromClass(class) => {
|
||||
class.find_legacy_typevars(db, typevars);
|
||||
class.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
Protocol::Synthesized(synthesized) => {
|
||||
synthesized.find_legacy_typevars(db, typevars);
|
||||
synthesized.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -385,8 +387,9 @@ impl<'db> Protocol<'db> {
|
|||
}
|
||||
|
||||
mod synthesized_protocol {
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::protocol_class::ProtocolInterface;
|
||||
use crate::types::{TypeMapping, TypeTransformer, TypeVarInstance, TypeVarVariance};
|
||||
use crate::types::{BoundTypeVarInstance, TypeMapping, TypeTransformer, TypeVarVariance};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
|
||||
|
@ -427,9 +430,10 @@ mod synthesized_protocol {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
self.0.find_legacy_typevars(db, typevars);
|
||||
self.0.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
pub(in crate::types) fn interface(self) -> ProtocolInterface<'db> {
|
||||
|
|
|
@ -222,7 +222,10 @@ impl ClassInfoConstraintFunction {
|
|||
Type::Union(union) => {
|
||||
union.try_map(db, |element| self.generate_constraint(db, *element))
|
||||
}
|
||||
Type::TypeVar(type_var) => match type_var.bound_or_constraints(db)? {
|
||||
Type::TypeVar(bound_typevar) => match bound_typevar
|
||||
.typevar(db)
|
||||
.bound_or_constraints(db)?
|
||||
{
|
||||
TypeVarBoundOrConstraints::UpperBound(bound) => self.generate_constraint(db, bound),
|
||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||
self.generate_constraint(db, Type::Union(constraints))
|
||||
|
|
|
@ -10,10 +10,11 @@ use crate::semantic_index::place_table;
|
|||
use crate::{
|
||||
Db, FxOrderSet,
|
||||
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
||||
semantic_index::use_def_map,
|
||||
semantic_index::{definition::Definition, use_def_map},
|
||||
types::{
|
||||
CallableType, ClassBase, ClassLiteral, KnownFunction, PropertyInstanceType, Signature,
|
||||
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeTransformer, TypeVarInstance,
|
||||
BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, KnownFunction,
|
||||
PropertyInstanceType, Signature, Type, TypeMapping, TypeQualifiers, TypeRelation,
|
||||
TypeTransformer,
|
||||
cyclic::PairVisitor,
|
||||
signatures::{Parameter, Parameters},
|
||||
},
|
||||
|
@ -210,10 +211,11 @@ impl<'db> ProtocolInterface<'db> {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
for data in self.inner(db).values() {
|
||||
data.find_legacy_typevars(db, typevars);
|
||||
data.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,9 +273,11 @@ impl<'db> ProtocolMemberData<'db> {
|
|||
fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
self.kind.find_legacy_typevars(db, typevars);
|
||||
self.kind
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
|
@ -358,12 +362,19 @@ impl<'db> ProtocolMemberKind<'db> {
|
|||
fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self {
|
||||
ProtocolMemberKind::Method(callable) => callable.find_legacy_typevars(db, typevars),
|
||||
ProtocolMemberKind::Property(property) => property.find_legacy_typevars(db, typevars),
|
||||
ProtocolMemberKind::Other(ty) => ty.find_legacy_typevars(db, typevars),
|
||||
ProtocolMemberKind::Method(callable) => {
|
||||
callable.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
ProtocolMemberKind::Property(property) => {
|
||||
property.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
ProtocolMemberKind::Other(ty) => {
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use smallvec::{SmallVec, smallvec_inline};
|
|||
use super::{DynamicType, Type, TypeTransformer, TypeVarVariance, definition_expression_type};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::generics::{GenericContext, walk_generic_context};
|
||||
use crate::types::{KnownClass, TypeMapping, TypeRelation, TypeVarInstance, todo_type};
|
||||
use crate::types::{BoundTypeVarInstance, KnownClass, TypeMapping, TypeRelation, todo_type};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
|
||||
|
@ -88,10 +88,11 @@ impl<'db> CallableSignature<'db> {
|
|||
pub(crate) fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
for signature in &self.overloads {
|
||||
signature.find_legacy_typevars(db, typevars);
|
||||
signature.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,18 +429,19 @@ impl<'db> Signature<'db> {
|
|||
pub(crate) fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
for param in &self.parameters {
|
||||
if let Some(ty) = param.annotated_type() {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
if let Some(ty) = param.default_type() {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
if let Some(ty) = self.return_ty {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use ruff_python_ast::name::Name;
|
||||
|
||||
use crate::place::PlaceAndQualifiers;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::{
|
||||
ClassType, DynamicType, KnownClass, MemberLookupPolicy, Type, TypeMapping, TypeRelation,
|
||||
TypeTransformer, TypeVarInstance,
|
||||
BindingContext, BoundTypeVarInstance, ClassType, DynamicType, KnownClass, MemberLookupPolicy,
|
||||
Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
@ -89,17 +90,20 @@ impl<'db> SubclassOfType<'db> {
|
|||
TypeVarVariance::Invariant => {
|
||||
// We need to materialize this to `type[T]` but that isn't representable so
|
||||
// we instead use a type variable with an upper bound of `type`.
|
||||
Type::TypeVar(TypeVarInstance::new(
|
||||
Type::TypeVar(BoundTypeVarInstance::new(
|
||||
db,
|
||||
Name::new_static("T_all"),
|
||||
None,
|
||||
None,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(
|
||||
KnownClass::Type.to_instance(db),
|
||||
)),
|
||||
variance,
|
||||
None,
|
||||
TypeVarKind::Pep695,
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
Name::new_static("T_all"),
|
||||
None,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(
|
||||
KnownClass::Type.to_instance(db),
|
||||
)),
|
||||
variance,
|
||||
None,
|
||||
TypeVarKind::Pep695,
|
||||
),
|
||||
BindingContext::Synthetic,
|
||||
))
|
||||
}
|
||||
TypeVarVariance::Bivariant => unreachable!(),
|
||||
|
@ -124,11 +128,12 @@ impl<'db> SubclassOfType<'db> {
|
|||
pub(super) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self.subclass_of {
|
||||
SubclassOfInner::Class(class) => {
|
||||
class.find_legacy_typevars(db, typevars);
|
||||
class.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
SubclassOfInner::Dynamic(_) => {}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,13 @@ use std::hash::Hash;
|
|||
|
||||
use itertools::{Either, EitherOrBoth, Itertools};
|
||||
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::class::{ClassType, KnownClass};
|
||||
use crate::types::{SubclassOfType, Truthiness};
|
||||
use crate::types::{
|
||||
Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance, TypeVarVariance,
|
||||
BoundTypeVarInstance, Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarVariance,
|
||||
UnionBuilder, UnionType, cyclic::PairVisitor,
|
||||
};
|
||||
use crate::types::{SubclassOfType, Truthiness};
|
||||
use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
@ -272,9 +273,11 @@ impl<'db> TupleType<'db> {
|
|||
pub(crate) fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
self.tuple(db).find_legacy_typevars(db, typevars);
|
||||
self.tuple(db)
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
|
||||
pub(crate) fn has_relation_to(
|
||||
|
@ -435,10 +438,11 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
|||
fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
for ty in &self.0 {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,14 +777,16 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
|||
fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
for ty in &self.prefix {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
self.variable.find_legacy_typevars(db, typevars);
|
||||
self.variable
|
||||
.find_legacy_typevars(db, binding_context, typevars);
|
||||
for ty in &self.suffix {
|
||||
ty.find_legacy_typevars(db, typevars);
|
||||
ty.find_legacy_typevars(db, binding_context, typevars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1120,11 +1126,12 @@ impl<'db> Tuple<Type<'db>> {
|
|||
fn find_legacy_typevars(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self {
|
||||
Tuple::Fixed(tuple) => tuple.find_legacy_typevars(db, typevars),
|
||||
Tuple::Variable(tuple) => tuple.find_legacy_typevars(db, typevars),
|
||||
Tuple::Fixed(tuple) => tuple.find_legacy_typevars(db, binding_context, typevars),
|
||||
Tuple::Variable(tuple) => tuple.find_legacy_typevars(db, binding_context, typevars),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
use crate::{
|
||||
Db, FxIndexSet,
|
||||
types::{
|
||||
BoundMethodType, BoundSuperType, CallableType, GenericAlias, IntersectionType,
|
||||
KnownInstanceType, MethodWrapperKind, NominalInstanceType, PropertyInstanceType,
|
||||
ProtocolInstanceType, SubclassOfType, Type, TypeAliasType, TypeIsType, TypeVarInstance,
|
||||
TypedDictType, UnionType,
|
||||
BoundMethodType, BoundSuperType, BoundTypeVarInstance, CallableType, GenericAlias,
|
||||
IntersectionType, KnownInstanceType, MethodWrapperKind, NominalInstanceType,
|
||||
PropertyInstanceType, ProtocolInstanceType, SubclassOfType, Type, TypeAliasType,
|
||||
TypeIsType, TypeVarInstance, TypedDictType, UnionType,
|
||||
class::walk_generic_alias,
|
||||
function::{FunctionType, walk_function_type},
|
||||
instance::{walk_nominal_instance_type, walk_protocol_instance_type},
|
||||
subclass_of::walk_subclass_of_type,
|
||||
tuple::{TupleType, walk_tuple_type},
|
||||
walk_bound_method_type, walk_bound_super_type, walk_callable_type, walk_intersection_type,
|
||||
walk_known_instance_type, walk_method_wrapper_type, walk_property_instance_type,
|
||||
walk_type_alias_type, walk_type_var_type, walk_typed_dict_type, walk_typeis_type,
|
||||
walk_union,
|
||||
walk_bound_method_type, walk_bound_super_type, walk_bound_type_var_type,
|
||||
walk_callable_type, walk_intersection_type, walk_known_instance_type,
|
||||
walk_method_wrapper_type, walk_property_instance_type, walk_type_alias_type,
|
||||
walk_type_var_type, walk_typed_dict_type, walk_typeis_type, walk_union,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -77,8 +77,16 @@ pub(crate) trait TypeVisitor<'db> {
|
|||
walk_nominal_instance_type(db, nominal, self);
|
||||
}
|
||||
|
||||
fn visit_type_var_type(&mut self, db: &'db dyn Db, type_var: TypeVarInstance<'db>) {
|
||||
walk_type_var_type(db, type_var, self);
|
||||
fn visit_bound_type_var_type(
|
||||
&mut self,
|
||||
db: &'db dyn Db,
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
) {
|
||||
walk_bound_type_var_type(db, bound_typevar, self);
|
||||
}
|
||||
|
||||
fn visit_type_var_type(&mut self, db: &'db dyn Db, typevar: TypeVarInstance<'db>) {
|
||||
walk_type_var_type(db, typevar, self);
|
||||
}
|
||||
|
||||
fn visit_protocol_instance_type(
|
||||
|
@ -131,7 +139,7 @@ enum NonAtomicType<'db> {
|
|||
NominalInstance(NominalInstanceType<'db>),
|
||||
PropertyInstance(PropertyInstanceType<'db>),
|
||||
TypeIs(TypeIsType<'db>),
|
||||
TypeVar(TypeVarInstance<'db>),
|
||||
TypeVar(BoundTypeVarInstance<'db>),
|
||||
ProtocolInstance(ProtocolInstanceType<'db>),
|
||||
TypedDict(TypedDictType<'db>),
|
||||
}
|
||||
|
@ -194,7 +202,9 @@ impl<'db> From<Type<'db>> for TypeKind<'db> {
|
|||
Type::PropertyInstance(property) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::PropertyInstance(property))
|
||||
}
|
||||
Type::TypeVar(type_var) => TypeKind::NonAtomic(NonAtomicType::TypeVar(type_var)),
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::TypeVar(bound_typevar))
|
||||
}
|
||||
Type::TypeIs(type_is) => TypeKind::NonAtomic(NonAtomicType::TypeIs(type_is)),
|
||||
Type::TypedDict(typed_dict) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::TypedDict(typed_dict))
|
||||
|
@ -231,7 +241,9 @@ fn walk_non_atomic_type<'db, V: TypeVisitor<'db> + ?Sized>(
|
|||
visitor.visit_property_instance_type(db, property);
|
||||
}
|
||||
NonAtomicType::TypeIs(type_is) => visitor.visit_typeis_type(db, type_is),
|
||||
NonAtomicType::TypeVar(type_var) => visitor.visit_type_var_type(db, type_var),
|
||||
NonAtomicType::TypeVar(bound_typevar) => {
|
||||
visitor.visit_bound_type_var_type(db, bound_typevar);
|
||||
}
|
||||
NonAtomicType::ProtocolInstance(protocol) => {
|
||||
visitor.visit_protocol_instance_type(db, protocol);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue