mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-27 18:36:35 +00:00
[ty] Add separate type for typevar "identity" (#20813)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
As part of #20598, we added `is_identical_to` methods to `TypeVarInstance` and `BoundTypeVarInstance`, which compare when two typevar instances refer to "the same" underlying typevar, even if we have forced their lazy bounds/constraints as part of marking typevars as inferable. (Doing so results in a different salsa interned struct ID, since we've changed the contents of the `bounds_or_constraints` field.) It turns out that marking typevars as inferable is not the only way that we might force lazy bounds/constraints; it also happens when we materialize a type containing a typevar. This surfaced as ecosystem report failures on #20677. That means that we need a more long-term fix to this problem. (`is_identical_to`, and its underlying `original` field, were meant to be a temporary fix until we removed the `MarkTypeVarsInferable` type mapping.) This PR extracts out a separate type (`TypeVarIdentity`) that only includes the fields that actually inform whether two typevars are "the same". All other properties of the typevar (default, bounds/constraints, etc) still live in `TypeVarInstance`. Call sites that care about typevar identity can now either store just `TypeVarIdentity` (if they never need access to those other properties), or continue to store `TypeVarInstance` but pull out its `identity` when performing those "are they the same typevar" comparisons. (All of this also applies respectively to `BoundTypeVar{Identity,Instance}`.) In particular, constraint sets now work on `BoundTypeVarIdentity`, and generic contexts still _store_ a `BoundTypeVarInstance` (since we might need access to defaults when specializing), but are keyed on `BoundTypeVarIdentity`.
This commit is contained in:
parent
aba0bd568e
commit
5e08e5451d
6 changed files with 199 additions and 169 deletions
|
|
@ -1674,7 +1674,7 @@ impl<'db> Type<'db> {
|
|||
(
|
||||
Type::NonInferableTypeVar(lhs_bound_typevar),
|
||||
Type::NonInferableTypeVar(rhs_bound_typevar),
|
||||
) if lhs_bound_typevar.is_identical_to(db, rhs_bound_typevar) => {
|
||||
) if lhs_bound_typevar.identity(db) == rhs_bound_typevar.identity(db) => {
|
||||
ConstraintSet::from(true)
|
||||
}
|
||||
|
||||
|
|
@ -2443,7 +2443,9 @@ impl<'db> Type<'db> {
|
|||
(
|
||||
Type::NonInferableTypeVar(self_bound_typevar),
|
||||
Type::NonInferableTypeVar(other_bound_typevar),
|
||||
) if self_bound_typevar == other_bound_typevar => ConstraintSet::from(false),
|
||||
) if self_bound_typevar.identity(db) == other_bound_typevar.identity(db) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
(tvar @ Type::NonInferableTypeVar(_), Type::Intersection(intersection))
|
||||
| (Type::Intersection(intersection), tvar @ Type::NonInferableTypeVar(_))
|
||||
|
|
@ -7789,7 +7791,32 @@ pub enum TypeVarKind {
|
|||
TypingSelf,
|
||||
}
|
||||
|
||||
/// A type variable that has not been bound to a generic context yet.
|
||||
/// The identity of a type variable.
|
||||
///
|
||||
/// This represents the core identity of a typevar, independent of its bounds or constraints. Two
|
||||
/// typevars have the same identity if they represent the same logical typevar, even if their
|
||||
/// bounds have been materialized differently.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the identity's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the identity was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct TypeVarIdentity<'db> {
|
||||
/// The name of this TypeVar (e.g. `T`)
|
||||
#[returns(ref)]
|
||||
pub(crate) name: ast::name::Name,
|
||||
|
||||
/// The type var's definition (None if synthesized)
|
||||
pub(crate) definition: Option<Definition<'db>>,
|
||||
|
||||
/// The kind of typevar (PEP 695, Legacy, or TypingSelf)
|
||||
pub(crate) kind: TypeVarKind,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for TypeVarIdentity<'_> {}
|
||||
|
||||
/// A specific instance of a type variable that has not been bound to a generic context yet.
|
||||
///
|
||||
/// 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
|
||||
|
|
@ -7828,12 +7855,8 @@ pub enum TypeVarKind {
|
|||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct TypeVarInstance<'db> {
|
||||
/// The name of this TypeVar (e.g. `T`)
|
||||
#[returns(ref)]
|
||||
name: ast::name::Name,
|
||||
|
||||
/// The type var's definition (None if synthesized)
|
||||
pub definition: Option<Definition<'db>>,
|
||||
/// The identity of this typevar
|
||||
pub(crate) identity: TypeVarIdentity<'db>,
|
||||
|
||||
/// The upper bound or constraint on the type of this TypeVar, if any. Don't use this field
|
||||
/// directly; use the `bound_or_constraints` (or `upper_bound` and `constraints`) methods
|
||||
|
|
@ -7846,14 +7869,6 @@ pub struct TypeVarInstance<'db> {
|
|||
/// The default type for this TypeVar, if any. Don't use this field directly, use the
|
||||
/// `default_type` method instead (to evaluate any lazy default).
|
||||
_default: Option<TypeVarDefaultEvaluation<'db>>,
|
||||
|
||||
pub kind: TypeVarKind,
|
||||
|
||||
/// If this typevar was transformed from another typevar via `mark_typevars_inferable`, this
|
||||
/// records the identity of the "original" typevar, so we can recognize them as the same
|
||||
/// typevar in `bind_typevar`. TODO: this (and the `is_identical_to` methods) should be
|
||||
/// removable once we remove `mark_typevars_inferable`.
|
||||
pub(crate) original: Option<TypeVarInstance<'db>>,
|
||||
}
|
||||
|
||||
// The Salsa heap is tracked separately.
|
||||
|
|
@ -7899,6 +7914,18 @@ impl<'db> TypeVarInstance<'db> {
|
|||
BoundTypeVarInstance::new(db, self, BindingContext::Definition(binding_context))
|
||||
}
|
||||
|
||||
pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
|
||||
self.identity(db).name(db)
|
||||
}
|
||||
|
||||
pub(crate) fn definition(self, db: &'db dyn Db) -> Option<Definition<'db>> {
|
||||
self.identity(db).definition(db)
|
||||
}
|
||||
|
||||
pub(crate) fn kind(self, db: &'db dyn Db) -> TypeVarKind {
|
||||
self.identity(db).kind(db)
|
||||
}
|
||||
|
||||
pub(crate) fn is_self(self, db: &'db dyn Db) -> bool {
|
||||
matches!(self.kind(db), TypeVarKind::TypingSelf)
|
||||
}
|
||||
|
|
@ -7942,8 +7969,7 @@ impl<'db> TypeVarInstance<'db> {
|
|||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.identity(db),
|
||||
self._bound_or_constraints(db)
|
||||
.and_then(|bound_or_constraints| match bound_or_constraints {
|
||||
TypeVarBoundOrConstraintsEvaluation::Eager(bound_or_constraints) => {
|
||||
|
|
@ -7963,8 +7989,6 @@ impl<'db> TypeVarInstance<'db> {
|
|||
.lazy_default(db)
|
||||
.map(|ty| ty.normalized_impl(db, visitor).into()),
|
||||
}),
|
||||
self.kind(db),
|
||||
self.original(db),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -7976,8 +8000,7 @@ impl<'db> TypeVarInstance<'db> {
|
|||
) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.identity(db),
|
||||
self._bound_or_constraints(db)
|
||||
.and_then(|bound_or_constraints| match bound_or_constraints {
|
||||
TypeVarBoundOrConstraintsEvaluation::Eager(bound_or_constraints) => Some(
|
||||
|
|
@ -8009,8 +8032,6 @@ impl<'db> TypeVarInstance<'db> {
|
|||
.lazy_default(db)
|
||||
.map(|ty| ty.materialize(db, materialization_kind, visitor).into()),
|
||||
}),
|
||||
self.kind(db),
|
||||
self.original(db),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -8047,33 +8068,15 @@ impl<'db> TypeVarInstance<'db> {
|
|||
}),
|
||||
});
|
||||
|
||||
// Ensure that we only modify the `original` field if we are going to modify one or both of
|
||||
// `_bound_or_constraints` and `_default`; don't trigger creation of a new
|
||||
// `TypeVarInstance` unnecessarily.
|
||||
let new_original = if new_bound_or_constraints == self._bound_or_constraints(db)
|
||||
&& new_default == self._default(db)
|
||||
{
|
||||
self.original(db)
|
||||
} else {
|
||||
Some(self)
|
||||
};
|
||||
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.identity(db),
|
||||
new_bound_or_constraints,
|
||||
self.explicit_variance(db),
|
||||
new_default,
|
||||
self.kind(db),
|
||||
new_original,
|
||||
)
|
||||
}
|
||||
|
||||
fn is_identical_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
self == other || (self.original(db) == Some(other) || other.original(db) == Some(self))
|
||||
}
|
||||
|
||||
fn to_instance(self, db: &'db dyn Db) -> Option<Self> {
|
||||
let bound_or_constraints = match self.bound_or_constraints(db)? {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound) => {
|
||||
|
|
@ -8083,15 +8086,18 @@ impl<'db> TypeVarInstance<'db> {
|
|||
TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?.into_union()?)
|
||||
}
|
||||
};
|
||||
Some(Self::new(
|
||||
let identity = TypeVarIdentity::new(
|
||||
db,
|
||||
Name::new(format!("{}'instance", self.name(db))),
|
||||
None,
|
||||
None, // definition
|
||||
self.kind(db),
|
||||
);
|
||||
Some(Self::new(
|
||||
db,
|
||||
identity,
|
||||
Some(bound_or_constraints.into()),
|
||||
self.explicit_variance(db),
|
||||
None,
|
||||
self.kind(db),
|
||||
self.original(db),
|
||||
None, // _default
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -8222,6 +8228,18 @@ impl<'db> BindingContext<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The identity of a bound type variable.
|
||||
///
|
||||
/// This identifies a specific binding of a typevar to a context (e.g., `T@ClassC` vs `T@FunctionF`),
|
||||
/// independent of the typevar's bounds or constraints. Two bound typevars have the same identity
|
||||
/// if they represent the same logical typevar bound in the same context, even if their bounds
|
||||
/// have been materialized differently.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
pub struct BoundTypeVarIdentity<'db> {
|
||||
pub(crate) identity: TypeVarIdentity<'db>,
|
||||
pub(crate) binding_context: BindingContext<'db>,
|
||||
}
|
||||
|
||||
/// A type variable that has been bound to a generic context, and which can be specialized to a
|
||||
/// concrete type.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
|
|
@ -8235,6 +8253,17 @@ pub struct BoundTypeVarInstance<'db> {
|
|||
impl get_size2::GetSize for BoundTypeVarInstance<'_> {}
|
||||
|
||||
impl<'db> BoundTypeVarInstance<'db> {
|
||||
/// Get the identity of this bound typevar.
|
||||
///
|
||||
/// This is used for comparing whether two bound typevars represent the same logical typevar,
|
||||
/// regardless of e.g. differences in their bounds or constraints due to materialization.
|
||||
pub(crate) fn identity(self, db: &'db dyn Db) -> BoundTypeVarIdentity<'db> {
|
||||
BoundTypeVarIdentity {
|
||||
identity: self.typevar(db).identity(db),
|
||||
binding_context: self.binding_context(db),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new PEP 695 type variable that can be used in signatures
|
||||
/// of synthetic generic functions.
|
||||
pub(crate) fn synthetic(
|
||||
|
|
@ -8242,20 +8271,20 @@ impl<'db> BoundTypeVarInstance<'db> {
|
|||
name: &'static str,
|
||||
variance: TypeVarVariance,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
let identity = TypeVarIdentity::new(
|
||||
db,
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
Name::new_static(name),
|
||||
None, // definition
|
||||
None, // _bound_or_constraints
|
||||
Some(variance),
|
||||
None, // _default
|
||||
TypeVarKind::Pep695,
|
||||
None,
|
||||
),
|
||||
BindingContext::Synthetic,
|
||||
)
|
||||
Name::new_static(name),
|
||||
None, // definition
|
||||
TypeVarKind::Pep695,
|
||||
);
|
||||
let typevar = TypeVarInstance::new(
|
||||
db,
|
||||
identity,
|
||||
None, // _bound_or_constraints
|
||||
Some(variance),
|
||||
None, // _default
|
||||
);
|
||||
Self::new(db, typevar, BindingContext::Synthetic)
|
||||
}
|
||||
|
||||
/// Create a new synthetic `Self` type variable with the given upper bound.
|
||||
|
|
@ -8264,32 +8293,20 @@ impl<'db> BoundTypeVarInstance<'db> {
|
|||
upper_bound: Type<'db>,
|
||||
binding_context: BindingContext<'db>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
let identity = TypeVarIdentity::new(
|
||||
db,
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
Name::new_static("Self"),
|
||||
None,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).into()),
|
||||
Some(TypeVarVariance::Invariant),
|
||||
None,
|
||||
TypeVarKind::TypingSelf,
|
||||
None,
|
||||
),
|
||||
binding_context,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn is_identical_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
if self == other {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.binding_context(db) != other.binding_context(db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.typevar(db).is_identical_to(db, other.typevar(db))
|
||||
Name::new_static("Self"),
|
||||
None, // definition
|
||||
TypeVarKind::TypingSelf,
|
||||
);
|
||||
let typevar = TypeVarInstance::new(
|
||||
db,
|
||||
identity,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).into()),
|
||||
Some(TypeVarVariance::Invariant),
|
||||
None, // _default
|
||||
);
|
||||
Self::new(db, typevar, binding_context)
|
||||
}
|
||||
|
||||
pub(crate) fn variance_with_polarity(
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ use itertools::Itertools;
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::Db;
|
||||
use crate::types::{BoundTypeVarInstance, IntersectionType, Type, UnionType};
|
||||
use crate::types::{BoundTypeVarIdentity, IntersectionType, Type, UnionType};
|
||||
|
||||
/// An extension trait for building constraint sets from [`Option`] values.
|
||||
pub(crate) trait OptionConstraintsExtension<T> {
|
||||
|
|
@ -223,7 +223,7 @@ impl<'db> ConstraintSet<'db> {
|
|||
pub(crate) fn range(
|
||||
db: &'db dyn Db,
|
||||
lower: Type<'db>,
|
||||
typevar: BoundTypeVarInstance<'db>,
|
||||
typevar: BoundTypeVarIdentity<'db>,
|
||||
upper: Type<'db>,
|
||||
) -> Self {
|
||||
let lower = lower.bottom_materialization(db);
|
||||
|
|
@ -236,7 +236,7 @@ impl<'db> ConstraintSet<'db> {
|
|||
pub(crate) fn negated_range(
|
||||
db: &'db dyn Db,
|
||||
lower: Type<'db>,
|
||||
typevar: BoundTypeVarInstance<'db>,
|
||||
typevar: BoundTypeVarIdentity<'db>,
|
||||
upper: Type<'db>,
|
||||
) -> Self {
|
||||
Self::range(db, lower, typevar, upper).negate(db)
|
||||
|
|
@ -258,7 +258,7 @@ impl From<bool> for ConstraintSet<'_> {
|
|||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub(crate) struct ConstrainedTypeVar<'db> {
|
||||
typevar: BoundTypeVarInstance<'db>,
|
||||
typevar: BoundTypeVarIdentity<'db>,
|
||||
lower: Type<'db>,
|
||||
upper: Type<'db>,
|
||||
}
|
||||
|
|
@ -274,7 +274,7 @@ impl<'db> ConstrainedTypeVar<'db> {
|
|||
fn new_node(
|
||||
db: &'db dyn Db,
|
||||
lower: Type<'db>,
|
||||
typevar: BoundTypeVarInstance<'db>,
|
||||
typevar: BoundTypeVarIdentity<'db>,
|
||||
upper: Type<'db>,
|
||||
) -> Node<'db> {
|
||||
debug_assert_eq!(lower, lower.bottom_materialization(db));
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
|
|||
use crate::types::tuple::TupleSpec;
|
||||
use crate::types::visitor::TypeVisitor;
|
||||
use crate::types::{
|
||||
BoundTypeVarInstance, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
|
||||
BoundTypeVarIdentity, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
|
||||
MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type,
|
||||
UnionType, WrapperDescriptorKind, visitor,
|
||||
};
|
||||
|
|
@ -561,7 +561,7 @@ impl Display for DisplayRepresentation<'_> {
|
|||
literal_name = enum_literal.name(self.db)
|
||||
),
|
||||
Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
|
||||
bound_typevar.display(self.db).fmt(f)
|
||||
bound_typevar.identity(self.db).display(self.db).fmt(f)
|
||||
}
|
||||
Type::AlwaysTruthy => f.write_str("AlwaysTruthy"),
|
||||
Type::AlwaysFalsy => f.write_str("AlwaysFalsy"),
|
||||
|
|
@ -598,24 +598,24 @@ impl Display for DisplayRepresentation<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'db> BoundTypeVarInstance<'db> {
|
||||
impl<'db> BoundTypeVarIdentity<'db> {
|
||||
pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
|
||||
DisplayBoundTypeVarInstance {
|
||||
bound_typevar: self,
|
||||
DisplayBoundTypeVarIdentity {
|
||||
bound_typevar_identity: self,
|
||||
db,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DisplayBoundTypeVarInstance<'db> {
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
struct DisplayBoundTypeVarIdentity<'db> {
|
||||
bound_typevar_identity: BoundTypeVarIdentity<'db>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl Display for DisplayBoundTypeVarInstance<'_> {
|
||||
impl Display for DisplayBoundTypeVarIdentity<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.bound_typevar.typevar(self.db).name(self.db))?;
|
||||
if let Some(binding_context) = self.bound_typevar.binding_context(self.db).name(self.db) {
|
||||
f.write_str(self.bound_typevar_identity.identity.name(self.db))?;
|
||||
if let Some(binding_context) = self.bound_typevar_identity.binding_context.name(self.db) {
|
||||
write!(f, "@{binding_context}")?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1730,7 +1730,7 @@ impl KnownFunction {
|
|||
return;
|
||||
};
|
||||
|
||||
let constraints = ConstraintSet::range(db, *lower, *typevar, *upper);
|
||||
let constraints = ConstraintSet::range(db, *lower, typevar.identity(db), *upper);
|
||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
|
||||
tracked,
|
||||
|
|
@ -1747,7 +1747,8 @@ impl KnownFunction {
|
|||
return;
|
||||
};
|
||||
|
||||
let constraints = ConstraintSet::negated_range(db, *lower, *typevar, *upper);
|
||||
let constraints =
|
||||
ConstraintSet::negated_range(db, *lower, typevar.identity(db), *upper);
|
||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
|
||||
tracked,
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ use crate::types::instance::{Protocol, ProtocolInstanceType};
|
|||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
|
||||
MaterializationKind, NormalizedVisitor, Type, TypeContext, TypeMapping, TypeRelation,
|
||||
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType,
|
||||
binding_type, declaration_type,
|
||||
ApplyTypeMappingVisitor, BoundTypeVarIdentity, BoundTypeVarInstance, ClassLiteral,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeContext,
|
||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity, TypeVarInstance,
|
||||
TypeVarKind, TypeVarVariance, UnionType, binding_type, declaration_type,
|
||||
};
|
||||
use crate::{Db, FxOrderMap, FxOrderSet};
|
||||
|
||||
|
|
@ -109,24 +109,25 @@ pub(crate) fn typing_self<'db>(
|
|||
) -> Option<Type<'db>> {
|
||||
let index = semantic_index(db, scope_id.file(db));
|
||||
|
||||
let typevar = TypeVarInstance::new(
|
||||
let identity = TypeVarIdentity::new(
|
||||
db,
|
||||
ast::name::Name::new_static("Self"),
|
||||
Some(class.definition(db)),
|
||||
Some(
|
||||
TypeVarBoundOrConstraints::UpperBound(Type::instance(
|
||||
db,
|
||||
class.identity_specialization(db, typevar_to_type),
|
||||
))
|
||||
.into(),
|
||||
),
|
||||
TypeVarKind::TypingSelf,
|
||||
);
|
||||
let bounds = TypeVarBoundOrConstraints::UpperBound(Type::instance(
|
||||
db,
|
||||
class.identity_specialization(db, typevar_to_type),
|
||||
));
|
||||
let typevar = TypeVarInstance::new(
|
||||
db,
|
||||
identity,
|
||||
Some(bounds.into()),
|
||||
// According to the [spec], we can consider `Self`
|
||||
// equivalent to an invariant type variable
|
||||
// [spec]: https://typing.python.org/en/latest/spec/generics.html#self
|
||||
Some(TypeVarVariance::Invariant),
|
||||
None,
|
||||
TypeVarKind::TypingSelf,
|
||||
None,
|
||||
);
|
||||
|
||||
bind_typevar(
|
||||
|
|
@ -139,12 +140,20 @@ pub(crate) fn typing_self<'db>(
|
|||
.map(typevar_to_type)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
pub struct GenericContextTypeVarOptions {
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
pub struct GenericContextTypeVar<'db> {
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
should_promote_literals: bool,
|
||||
}
|
||||
|
||||
impl GenericContextTypeVarOptions {
|
||||
impl<'db> GenericContextTypeVar<'db> {
|
||||
fn new(bound_typevar: BoundTypeVarInstance<'db>) -> Self {
|
||||
Self {
|
||||
bound_typevar,
|
||||
should_promote_literals: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn promote_literals(mut self) -> Self {
|
||||
self.should_promote_literals = true;
|
||||
self
|
||||
|
|
@ -160,7 +169,7 @@ impl GenericContextTypeVarOptions {
|
|||
#[derive(PartialOrd, Ord)]
|
||||
pub struct GenericContext<'db> {
|
||||
#[returns(ref)]
|
||||
variables_inner: FxOrderMap<BoundTypeVarInstance<'db>, GenericContextTypeVarOptions>,
|
||||
variables_inner: FxOrderMap<BoundTypeVarIdentity<'db>, GenericContextTypeVar<'db>>,
|
||||
}
|
||||
|
||||
pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
|
@ -179,9 +188,15 @@ impl get_size2::GetSize for GenericContext<'_> {}
|
|||
impl<'db> GenericContext<'db> {
|
||||
fn from_variables(
|
||||
db: &'db dyn Db,
|
||||
variables: impl IntoIterator<Item = (BoundTypeVarInstance<'db>, GenericContextTypeVarOptions)>,
|
||||
variables: impl IntoIterator<Item = GenericContextTypeVar<'db>>,
|
||||
) -> Self {
|
||||
Self::new_internal(db, variables.into_iter().collect::<FxOrderMap<_, _>>())
|
||||
Self::new_internal(
|
||||
db,
|
||||
variables
|
||||
.into_iter()
|
||||
.map(|variable| (variable.bound_typevar.identity(db), variable))
|
||||
.collect::<FxOrderMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a generic context from a list of PEP-695 type parameters.
|
||||
|
|
@ -203,12 +218,7 @@ impl<'db> GenericContext<'db> {
|
|||
db: &'db dyn Db,
|
||||
type_params: impl IntoIterator<Item = BoundTypeVarInstance<'db>>,
|
||||
) -> Self {
|
||||
Self::from_variables(
|
||||
db,
|
||||
type_params
|
||||
.into_iter()
|
||||
.map(|bound_typevar| (bound_typevar, GenericContextTypeVarOptions::default())),
|
||||
)
|
||||
Self::from_variables(db, type_params.into_iter().map(GenericContextTypeVar::new))
|
||||
}
|
||||
|
||||
/// Returns a copy of this generic context where we will promote literal types in any inferred
|
||||
|
|
@ -217,8 +227,8 @@ impl<'db> GenericContext<'db> {
|
|||
Self::from_variables(
|
||||
db,
|
||||
self.variables_inner(db)
|
||||
.iter()
|
||||
.map(|(bound_typevar, options)| (*bound_typevar, options.promote_literals())),
|
||||
.values()
|
||||
.map(|variable| variable.promote_literals()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +238,9 @@ impl<'db> GenericContext<'db> {
|
|||
Self::from_variables(
|
||||
db,
|
||||
self.variables_inner(db)
|
||||
.iter()
|
||||
.chain(other.variables_inner(db).iter())
|
||||
.map(|(bound_typevar, options)| (*bound_typevar, *options)),
|
||||
.values()
|
||||
.chain(other.variables_inner(db).values())
|
||||
.copied(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +248,9 @@ impl<'db> GenericContext<'db> {
|
|||
self,
|
||||
db: &'db dyn Db,
|
||||
) -> impl ExactSizeIterator<Item = BoundTypeVarInstance<'db>> + Clone {
|
||||
self.variables_inner(db).keys().copied()
|
||||
self.variables_inner(db)
|
||||
.values()
|
||||
.map(|variable| variable.bound_typevar)
|
||||
}
|
||||
|
||||
fn variable_from_type_param(
|
||||
|
|
@ -411,7 +423,7 @@ impl<'db> GenericContext<'db> {
|
|||
pub(crate) fn is_subset_of(self, db: &'db dyn Db, other: GenericContext<'db>) -> bool {
|
||||
let other_variables = other.variables_inner(db);
|
||||
self.variables(db)
|
||||
.all(|bound_typevar| other_variables.contains_key(&bound_typevar))
|
||||
.all(|bound_typevar| other_variables.contains_key(&bound_typevar.identity(db)))
|
||||
}
|
||||
|
||||
pub(crate) fn binds_named_typevar(
|
||||
|
|
@ -428,8 +440,9 @@ impl<'db> GenericContext<'db> {
|
|||
db: &'db dyn Db,
|
||||
typevar: TypeVarInstance<'db>,
|
||||
) -> Option<BoundTypeVarInstance<'db>> {
|
||||
self.variables(db)
|
||||
.find(|self_bound_typevar| self_bound_typevar.typevar(db).is_identical_to(db, typevar))
|
||||
self.variables(db).find(|self_bound_typevar| {
|
||||
self_bound_typevar.typevar(db).identity(db) == typevar.identity(db)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a specialization of this generic context. Panics if the length of `types` does not
|
||||
|
|
@ -513,7 +526,7 @@ impl<'db> GenericContext<'db> {
|
|||
}
|
||||
|
||||
fn heap_size(
|
||||
(variables,): &(FxOrderMap<BoundTypeVarInstance<'db>, GenericContextTypeVarOptions>,),
|
||||
(variables,): &(FxOrderMap<BoundTypeVarIdentity<'db>, GenericContextTypeVar<'db>>,),
|
||||
) -> usize {
|
||||
ruff_memory_usage::order_map_heap_size(variables)
|
||||
}
|
||||
|
|
@ -765,7 +778,7 @@ impl<'db> Specialization<'db> {
|
|||
let restricted_variables = generic_context.variables(db);
|
||||
let restricted_types: Option<Box<[_]>> = restricted_variables
|
||||
.map(|variable| {
|
||||
let index = self_variables.get_index_of(&variable)?;
|
||||
let index = self_variables.get_index_of(&variable.identity(db))?;
|
||||
self_types.get(index).copied()
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -793,7 +806,7 @@ impl<'db> Specialization<'db> {
|
|||
let index = self
|
||||
.generic_context(db)
|
||||
.variables_inner(db)
|
||||
.get_index_of(&bound_typevar)?;
|
||||
.get_index_of(&bound_typevar.identity(db))?;
|
||||
self.types(db).get(index).copied()
|
||||
}
|
||||
|
||||
|
|
@ -1146,7 +1159,7 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
|||
let index = self
|
||||
.generic_context
|
||||
.variables_inner(db)
|
||||
.get_index_of(&bound_typevar)?;
|
||||
.get_index_of(&bound_typevar.identity(db))?;
|
||||
self.types.get(index).copied()
|
||||
}
|
||||
}
|
||||
|
|
@ -1155,7 +1168,7 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
|||
/// specialization of a generic function.
|
||||
pub(crate) struct SpecializationBuilder<'db> {
|
||||
db: &'db dyn Db,
|
||||
types: FxHashMap<BoundTypeVarInstance<'db>, Type<'db>>,
|
||||
types: FxHashMap<BoundTypeVarIdentity<'db>, Type<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> SpecializationBuilder<'db> {
|
||||
|
|
@ -1175,20 +1188,21 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
.annotation
|
||||
.and_then(|annotation| annotation.specialization_of(self.db, None));
|
||||
|
||||
let types = (generic_context.variables_inner(self.db).iter()).map(|(variable, options)| {
|
||||
let mut ty = self.types.get(variable).copied();
|
||||
let types =
|
||||
(generic_context.variables_inner(self.db).iter()).map(|(identity, variable)| {
|
||||
let mut ty = self.types.get(identity).copied();
|
||||
|
||||
// When inferring a specialization for a generic class typevar from a constructor call,
|
||||
// promote any typevars that are inferred as a literal to the corresponding instance type.
|
||||
if options.should_promote_literals {
|
||||
let tcx = tcx_specialization
|
||||
.and_then(|specialization| specialization.get(self.db, *variable));
|
||||
// When inferring a specialization for a generic class typevar from a constructor call,
|
||||
// promote any typevars that are inferred as a literal to the corresponding instance type.
|
||||
if variable.should_promote_literals {
|
||||
let tcx = tcx_specialization.and_then(|specialization| {
|
||||
specialization.get(self.db, variable.bound_typevar)
|
||||
});
|
||||
ty = ty.map(|ty| ty.promote_literals(self.db, TypeContext::new(tcx)));
|
||||
}
|
||||
|
||||
ty = ty.map(|ty| ty.promote_literals(self.db, TypeContext::new(tcx)));
|
||||
}
|
||||
|
||||
ty
|
||||
});
|
||||
ty
|
||||
});
|
||||
|
||||
// TODO Infer the tuple spec for a tuple type
|
||||
generic_context.specialize_partial(self.db, types)
|
||||
|
|
@ -1196,7 +1210,7 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
|
||||
fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
|
||||
self.types
|
||||
.entry(bound_typevar)
|
||||
.entry(bound_typevar.identity(self.db))
|
||||
.and_modify(|existing| {
|
||||
*existing = UnionType::from_elements(self.db, [*existing, ty]);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -94,8 +94,9 @@ use crate::types::{
|
|||
MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm,
|
||||
Parameters, SpecialFormType, SubclassOfType, TrackedConstraintSet, Truthiness, Type,
|
||||
TypeAliasType, TypeAndQualifiers, TypeContext, TypeQualifiers,
|
||||
TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarInstance, TypeVarKind,
|
||||
TypeVarVariance, TypedDictType, UnionBuilder, UnionType, binding_type, todo_type,
|
||||
TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarIdentity,
|
||||
TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder, UnionType,
|
||||
binding_type, todo_type,
|
||||
};
|
||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
||||
use crate::unpack::{EvaluationMode, UnpackPosition};
|
||||
|
|
@ -3001,15 +3002,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
if bound_or_constraint.is_some() || default.is_some() {
|
||||
self.deferred.insert(definition);
|
||||
}
|
||||
let identity =
|
||||
TypeVarIdentity::new(self.db(), &name.id, Some(definition), TypeVarKind::Pep695);
|
||||
let ty = Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
|
||||
self.db(),
|
||||
&name.id,
|
||||
Some(definition),
|
||||
identity,
|
||||
bound_or_constraint,
|
||||
None,
|
||||
None, // explicit_variance
|
||||
default.as_deref().map(|_| TypeVarDefaultEvaluation::Lazy),
|
||||
TypeVarKind::Pep695,
|
||||
None,
|
||||
)));
|
||||
self.add_declaration_with_binding(
|
||||
node.into(),
|
||||
|
|
@ -4340,15 +4340,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.deferred.insert(definition);
|
||||
}
|
||||
|
||||
let identity = TypeVarIdentity::new(db, target_name, Some(definition), TypeVarKind::Legacy);
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
|
||||
db,
|
||||
target_name,
|
||||
Some(definition),
|
||||
identity,
|
||||
bound_or_constraints,
|
||||
Some(variance),
|
||||
default,
|
||||
TypeVarKind::Legacy,
|
||||
None,
|
||||
)))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue