mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-11-03 21:24:29 +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(lhs_bound_typevar),
 | 
				
			||||||
                Type::NonInferableTypeVar(rhs_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)
 | 
					                ConstraintSet::from(true)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2443,7 +2443,9 @@ impl<'db> Type<'db> {
 | 
				
			||||||
            (
 | 
					            (
 | 
				
			||||||
                Type::NonInferableTypeVar(self_bound_typevar),
 | 
					                Type::NonInferableTypeVar(self_bound_typevar),
 | 
				
			||||||
                Type::NonInferableTypeVar(other_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))
 | 
					            (tvar @ Type::NonInferableTypeVar(_), Type::Intersection(intersection))
 | 
				
			||||||
            | (Type::Intersection(intersection), tvar @ Type::NonInferableTypeVar(_))
 | 
					            | (Type::Intersection(intersection), tvar @ Type::NonInferableTypeVar(_))
 | 
				
			||||||
| 
						 | 
					@ -7789,7 +7791,32 @@ pub enum TypeVarKind {
 | 
				
			||||||
    TypingSelf,
 | 
					    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
 | 
					/// 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
 | 
					/// 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)]
 | 
					#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
 | 
				
			||||||
#[derive(PartialOrd, Ord)]
 | 
					#[derive(PartialOrd, Ord)]
 | 
				
			||||||
pub struct TypeVarInstance<'db> {
 | 
					pub struct TypeVarInstance<'db> {
 | 
				
			||||||
    /// The name of this TypeVar (e.g. `T`)
 | 
					    /// The identity of this typevar
 | 
				
			||||||
    #[returns(ref)]
 | 
					    pub(crate) identity: TypeVarIdentity<'db>,
 | 
				
			||||||
    name: ast::name::Name,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// The type var's definition (None if synthesized)
 | 
					 | 
				
			||||||
    pub definition: Option<Definition<'db>>,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// The upper bound or constraint on the type of this TypeVar, if any. Don't use this field
 | 
					    /// 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
 | 
					    /// 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
 | 
					    /// 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_type` method instead (to evaluate any lazy default).
 | 
				
			||||||
    _default: Option<TypeVarDefaultEvaluation<'db>>,
 | 
					    _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.
 | 
					// The Salsa heap is tracked separately.
 | 
				
			||||||
| 
						 | 
					@ -7899,6 +7914,18 @@ impl<'db> TypeVarInstance<'db> {
 | 
				
			||||||
        BoundTypeVarInstance::new(db, self, BindingContext::Definition(binding_context))
 | 
					        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 {
 | 
					    pub(crate) fn is_self(self, db: &'db dyn Db) -> bool {
 | 
				
			||||||
        matches!(self.kind(db), TypeVarKind::TypingSelf)
 | 
					        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 {
 | 
					    pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
 | 
				
			||||||
        Self::new(
 | 
					        Self::new(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            self.name(db),
 | 
					            self.identity(db),
 | 
				
			||||||
            self.definition(db),
 | 
					 | 
				
			||||||
            self._bound_or_constraints(db)
 | 
					            self._bound_or_constraints(db)
 | 
				
			||||||
                .and_then(|bound_or_constraints| match bound_or_constraints {
 | 
					                .and_then(|bound_or_constraints| match bound_or_constraints {
 | 
				
			||||||
                    TypeVarBoundOrConstraintsEvaluation::Eager(bound_or_constraints) => {
 | 
					                    TypeVarBoundOrConstraintsEvaluation::Eager(bound_or_constraints) => {
 | 
				
			||||||
| 
						 | 
					@ -7963,8 +7989,6 @@ impl<'db> TypeVarInstance<'db> {
 | 
				
			||||||
                    .lazy_default(db)
 | 
					                    .lazy_default(db)
 | 
				
			||||||
                    .map(|ty| ty.normalized_impl(db, visitor).into()),
 | 
					                    .map(|ty| ty.normalized_impl(db, visitor).into()),
 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
            self.kind(db),
 | 
					 | 
				
			||||||
            self.original(db),
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7976,8 +8000,7 @@ impl<'db> TypeVarInstance<'db> {
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self::new(
 | 
					        Self::new(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            self.name(db),
 | 
					            self.identity(db),
 | 
				
			||||||
            self.definition(db),
 | 
					 | 
				
			||||||
            self._bound_or_constraints(db)
 | 
					            self._bound_or_constraints(db)
 | 
				
			||||||
                .and_then(|bound_or_constraints| match bound_or_constraints {
 | 
					                .and_then(|bound_or_constraints| match bound_or_constraints {
 | 
				
			||||||
                    TypeVarBoundOrConstraintsEvaluation::Eager(bound_or_constraints) => Some(
 | 
					                    TypeVarBoundOrConstraintsEvaluation::Eager(bound_or_constraints) => Some(
 | 
				
			||||||
| 
						 | 
					@ -8009,8 +8032,6 @@ impl<'db> TypeVarInstance<'db> {
 | 
				
			||||||
                    .lazy_default(db)
 | 
					                    .lazy_default(db)
 | 
				
			||||||
                    .map(|ty| ty.materialize(db, materialization_kind, visitor).into()),
 | 
					                    .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(
 | 
					        Self::new(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            self.name(db),
 | 
					            self.identity(db),
 | 
				
			||||||
            self.definition(db),
 | 
					 | 
				
			||||||
            new_bound_or_constraints,
 | 
					            new_bound_or_constraints,
 | 
				
			||||||
            self.explicit_variance(db),
 | 
					            self.explicit_variance(db),
 | 
				
			||||||
            new_default,
 | 
					            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> {
 | 
					    fn to_instance(self, db: &'db dyn Db) -> Option<Self> {
 | 
				
			||||||
        let bound_or_constraints = match self.bound_or_constraints(db)? {
 | 
					        let bound_or_constraints = match self.bound_or_constraints(db)? {
 | 
				
			||||||
            TypeVarBoundOrConstraints::UpperBound(upper_bound) => {
 | 
					            TypeVarBoundOrConstraints::UpperBound(upper_bound) => {
 | 
				
			||||||
| 
						 | 
					@ -8083,15 +8086,18 @@ impl<'db> TypeVarInstance<'db> {
 | 
				
			||||||
                TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?.into_union()?)
 | 
					                TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?.into_union()?)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        Some(Self::new(
 | 
					        let identity = TypeVarIdentity::new(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            Name::new(format!("{}'instance", self.name(db))),
 | 
					            Name::new(format!("{}'instance", self.name(db))),
 | 
				
			||||||
            None,
 | 
					            None, // definition
 | 
				
			||||||
 | 
					            self.kind(db),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        Some(Self::new(
 | 
				
			||||||
 | 
					            db,
 | 
				
			||||||
 | 
					            identity,
 | 
				
			||||||
            Some(bound_or_constraints.into()),
 | 
					            Some(bound_or_constraints.into()),
 | 
				
			||||||
            self.explicit_variance(db),
 | 
					            self.explicit_variance(db),
 | 
				
			||||||
            None,
 | 
					            None, // _default
 | 
				
			||||||
            self.kind(db),
 | 
					 | 
				
			||||||
            self.original(db),
 | 
					 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					/// A type variable that has been bound to a generic context, and which can be specialized to a
 | 
				
			||||||
/// concrete type.
 | 
					/// concrete type.
 | 
				
			||||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
 | 
					#[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 get_size2::GetSize for BoundTypeVarInstance<'_> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'db> BoundTypeVarInstance<'db> {
 | 
					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
 | 
					    /// Create a new PEP 695 type variable that can be used in signatures
 | 
				
			||||||
    /// of synthetic generic functions.
 | 
					    /// of synthetic generic functions.
 | 
				
			||||||
    pub(crate) fn synthetic(
 | 
					    pub(crate) fn synthetic(
 | 
				
			||||||
| 
						 | 
					@ -8242,20 +8271,20 @@ impl<'db> BoundTypeVarInstance<'db> {
 | 
				
			||||||
        name: &'static str,
 | 
					        name: &'static str,
 | 
				
			||||||
        variance: TypeVarVariance,
 | 
					        variance: TypeVarVariance,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self::new(
 | 
					        let identity = TypeVarIdentity::new(
 | 
				
			||||||
            db,
 | 
					 | 
				
			||||||
            TypeVarInstance::new(
 | 
					 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            Name::new_static(name),
 | 
					            Name::new_static(name),
 | 
				
			||||||
            None, // definition
 | 
					            None, // definition
 | 
				
			||||||
 | 
					            TypeVarKind::Pep695,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let typevar = TypeVarInstance::new(
 | 
				
			||||||
 | 
					            db,
 | 
				
			||||||
 | 
					            identity,
 | 
				
			||||||
            None, // _bound_or_constraints
 | 
					            None, // _bound_or_constraints
 | 
				
			||||||
            Some(variance),
 | 
					            Some(variance),
 | 
				
			||||||
            None, // _default
 | 
					            None, // _default
 | 
				
			||||||
                TypeVarKind::Pep695,
 | 
					        );
 | 
				
			||||||
                None,
 | 
					        Self::new(db, typevar, BindingContext::Synthetic)
 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            BindingContext::Synthetic,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Create a new synthetic `Self` type variable with the given upper bound.
 | 
					    /// Create a new synthetic `Self` type variable with the given upper bound.
 | 
				
			||||||
| 
						 | 
					@ -8264,32 +8293,20 @@ impl<'db> BoundTypeVarInstance<'db> {
 | 
				
			||||||
        upper_bound: Type<'db>,
 | 
					        upper_bound: Type<'db>,
 | 
				
			||||||
        binding_context: BindingContext<'db>,
 | 
					        binding_context: BindingContext<'db>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self::new(
 | 
					        let identity = TypeVarIdentity::new(
 | 
				
			||||||
            db,
 | 
					 | 
				
			||||||
            TypeVarInstance::new(
 | 
					 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            Name::new_static("Self"),
 | 
					            Name::new_static("Self"),
 | 
				
			||||||
                None,
 | 
					            None, // definition
 | 
				
			||||||
 | 
					            TypeVarKind::TypingSelf,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let typevar = TypeVarInstance::new(
 | 
				
			||||||
 | 
					            db,
 | 
				
			||||||
 | 
					            identity,
 | 
				
			||||||
            Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).into()),
 | 
					            Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).into()),
 | 
				
			||||||
            Some(TypeVarVariance::Invariant),
 | 
					            Some(TypeVarVariance::Invariant),
 | 
				
			||||||
                None,
 | 
					            None, // _default
 | 
				
			||||||
                TypeVarKind::TypingSelf,
 | 
					        );
 | 
				
			||||||
                None,
 | 
					        Self::new(db, typevar, binding_context)
 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            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))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn variance_with_polarity(
 | 
					    pub(crate) fn variance_with_polarity(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ use itertools::Itertools;
 | 
				
			||||||
use rustc_hash::FxHashSet;
 | 
					use rustc_hash::FxHashSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::Db;
 | 
					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.
 | 
					/// An extension trait for building constraint sets from [`Option`] values.
 | 
				
			||||||
pub(crate) trait OptionConstraintsExtension<T> {
 | 
					pub(crate) trait OptionConstraintsExtension<T> {
 | 
				
			||||||
| 
						 | 
					@ -223,7 +223,7 @@ impl<'db> ConstraintSet<'db> {
 | 
				
			||||||
    pub(crate) fn range(
 | 
					    pub(crate) fn range(
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
        lower: Type<'db>,
 | 
					        lower: Type<'db>,
 | 
				
			||||||
        typevar: BoundTypeVarInstance<'db>,
 | 
					        typevar: BoundTypeVarIdentity<'db>,
 | 
				
			||||||
        upper: Type<'db>,
 | 
					        upper: Type<'db>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        let lower = lower.bottom_materialization(db);
 | 
					        let lower = lower.bottom_materialization(db);
 | 
				
			||||||
| 
						 | 
					@ -236,7 +236,7 @@ impl<'db> ConstraintSet<'db> {
 | 
				
			||||||
    pub(crate) fn negated_range(
 | 
					    pub(crate) fn negated_range(
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
        lower: Type<'db>,
 | 
					        lower: Type<'db>,
 | 
				
			||||||
        typevar: BoundTypeVarInstance<'db>,
 | 
					        typevar: BoundTypeVarIdentity<'db>,
 | 
				
			||||||
        upper: Type<'db>,
 | 
					        upper: Type<'db>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self::range(db, lower, typevar, upper).negate(db)
 | 
					        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)]
 | 
					#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
 | 
				
			||||||
#[derive(PartialOrd, Ord)]
 | 
					#[derive(PartialOrd, Ord)]
 | 
				
			||||||
pub(crate) struct ConstrainedTypeVar<'db> {
 | 
					pub(crate) struct ConstrainedTypeVar<'db> {
 | 
				
			||||||
    typevar: BoundTypeVarInstance<'db>,
 | 
					    typevar: BoundTypeVarIdentity<'db>,
 | 
				
			||||||
    lower: Type<'db>,
 | 
					    lower: Type<'db>,
 | 
				
			||||||
    upper: Type<'db>,
 | 
					    upper: Type<'db>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -274,7 +274,7 @@ impl<'db> ConstrainedTypeVar<'db> {
 | 
				
			||||||
    fn new_node(
 | 
					    fn new_node(
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
        lower: Type<'db>,
 | 
					        lower: Type<'db>,
 | 
				
			||||||
        typevar: BoundTypeVarInstance<'db>,
 | 
					        typevar: BoundTypeVarIdentity<'db>,
 | 
				
			||||||
        upper: Type<'db>,
 | 
					        upper: Type<'db>,
 | 
				
			||||||
    ) -> Node<'db> {
 | 
					    ) -> Node<'db> {
 | 
				
			||||||
        debug_assert_eq!(lower, lower.bottom_materialization(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::tuple::TupleSpec;
 | 
				
			||||||
use crate::types::visitor::TypeVisitor;
 | 
					use crate::types::visitor::TypeVisitor;
 | 
				
			||||||
use crate::types::{
 | 
					use crate::types::{
 | 
				
			||||||
    BoundTypeVarInstance, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
 | 
					    BoundTypeVarIdentity, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
 | 
				
			||||||
    MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type,
 | 
					    MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type,
 | 
				
			||||||
    UnionType, WrapperDescriptorKind, visitor,
 | 
					    UnionType, WrapperDescriptorKind, visitor,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -561,7 +561,7 @@ impl Display for DisplayRepresentation<'_> {
 | 
				
			||||||
                literal_name = enum_literal.name(self.db)
 | 
					                literal_name = enum_literal.name(self.db)
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
 | 
					            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::AlwaysTruthy => f.write_str("AlwaysTruthy"),
 | 
				
			||||||
            Type::AlwaysFalsy => f.write_str("AlwaysFalsy"),
 | 
					            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 {
 | 
					    pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
 | 
				
			||||||
        DisplayBoundTypeVarInstance {
 | 
					        DisplayBoundTypeVarIdentity {
 | 
				
			||||||
            bound_typevar: self,
 | 
					            bound_typevar_identity: self,
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct DisplayBoundTypeVarInstance<'db> {
 | 
					struct DisplayBoundTypeVarIdentity<'db> {
 | 
				
			||||||
    bound_typevar: BoundTypeVarInstance<'db>,
 | 
					    bound_typevar_identity: BoundTypeVarIdentity<'db>,
 | 
				
			||||||
    db: &'db dyn Db,
 | 
					    db: &'db dyn Db,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for DisplayBoundTypeVarInstance<'_> {
 | 
					impl Display for DisplayBoundTypeVarIdentity<'_> {
 | 
				
			||||||
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 | 
					    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
        f.write_str(self.bound_typevar.typevar(self.db).name(self.db))?;
 | 
					        f.write_str(self.bound_typevar_identity.identity.name(self.db))?;
 | 
				
			||||||
        if let Some(binding_context) = self.bound_typevar.binding_context(self.db).name(self.db) {
 | 
					        if let Some(binding_context) = self.bound_typevar_identity.binding_context.name(self.db) {
 | 
				
			||||||
            write!(f, "@{binding_context}")?;
 | 
					            write!(f, "@{binding_context}")?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1730,7 +1730,7 @@ impl KnownFunction {
 | 
				
			||||||
                    return;
 | 
					                    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);
 | 
					                let tracked = TrackedConstraintSet::new(db, constraints);
 | 
				
			||||||
                overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
 | 
					                overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
 | 
				
			||||||
                    tracked,
 | 
					                    tracked,
 | 
				
			||||||
| 
						 | 
					@ -1747,7 +1747,8 @@ impl KnownFunction {
 | 
				
			||||||
                    return;
 | 
					                    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);
 | 
					                let tracked = TrackedConstraintSet::new(db, constraints);
 | 
				
			||||||
                overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
 | 
					                overload.set_return_type(Type::KnownInstance(KnownInstanceType::ConstraintSet(
 | 
				
			||||||
                    tracked,
 | 
					                    tracked,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,11 +14,11 @@ use crate::types::instance::{Protocol, ProtocolInstanceType};
 | 
				
			||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
 | 
					use crate::types::signatures::{Parameter, Parameters, Signature};
 | 
				
			||||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
 | 
					use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
 | 
				
			||||||
use crate::types::{
 | 
					use crate::types::{
 | 
				
			||||||
    ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
 | 
					    ApplyTypeMappingVisitor, BoundTypeVarIdentity, BoundTypeVarInstance, ClassLiteral,
 | 
				
			||||||
    HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
 | 
					    FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
 | 
				
			||||||
    MaterializationKind, NormalizedVisitor, Type, TypeContext, TypeMapping, TypeRelation,
 | 
					    KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeContext,
 | 
				
			||||||
    TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType,
 | 
					    TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity, TypeVarInstance,
 | 
				
			||||||
    binding_type, declaration_type,
 | 
					    TypeVarKind, TypeVarVariance, UnionType, binding_type, declaration_type,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use crate::{Db, FxOrderMap, FxOrderSet};
 | 
					use crate::{Db, FxOrderMap, FxOrderSet};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,24 +109,25 @@ pub(crate) fn typing_self<'db>(
 | 
				
			||||||
) -> Option<Type<'db>> {
 | 
					) -> Option<Type<'db>> {
 | 
				
			||||||
    let index = semantic_index(db, scope_id.file(db));
 | 
					    let index = semantic_index(db, scope_id.file(db));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let typevar = TypeVarInstance::new(
 | 
					    let identity = TypeVarIdentity::new(
 | 
				
			||||||
        db,
 | 
					        db,
 | 
				
			||||||
        ast::name::Name::new_static("Self"),
 | 
					        ast::name::Name::new_static("Self"),
 | 
				
			||||||
        Some(class.definition(db)),
 | 
					        Some(class.definition(db)),
 | 
				
			||||||
        Some(
 | 
					        TypeVarKind::TypingSelf,
 | 
				
			||||||
            TypeVarBoundOrConstraints::UpperBound(Type::instance(
 | 
					    );
 | 
				
			||||||
 | 
					    let bounds = TypeVarBoundOrConstraints::UpperBound(Type::instance(
 | 
				
			||||||
        db,
 | 
					        db,
 | 
				
			||||||
        class.identity_specialization(db, typevar_to_type),
 | 
					        class.identity_specialization(db, typevar_to_type),
 | 
				
			||||||
            ))
 | 
					    ));
 | 
				
			||||||
            .into(),
 | 
					    let typevar = TypeVarInstance::new(
 | 
				
			||||||
        ),
 | 
					        db,
 | 
				
			||||||
 | 
					        identity,
 | 
				
			||||||
 | 
					        Some(bounds.into()),
 | 
				
			||||||
        // According to the [spec], we can consider `Self`
 | 
					        // According to the [spec], we can consider `Self`
 | 
				
			||||||
        // equivalent to an invariant type variable
 | 
					        // equivalent to an invariant type variable
 | 
				
			||||||
        // [spec]: https://typing.python.org/en/latest/spec/generics.html#self
 | 
					        // [spec]: https://typing.python.org/en/latest/spec/generics.html#self
 | 
				
			||||||
        Some(TypeVarVariance::Invariant),
 | 
					        Some(TypeVarVariance::Invariant),
 | 
				
			||||||
        None,
 | 
					        None,
 | 
				
			||||||
        TypeVarKind::TypingSelf,
 | 
					 | 
				
			||||||
        None,
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bind_typevar(
 | 
					    bind_typevar(
 | 
				
			||||||
| 
						 | 
					@ -139,12 +140,20 @@ pub(crate) fn typing_self<'db>(
 | 
				
			||||||
    .map(typevar_to_type)
 | 
					    .map(typevar_to_type)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, get_size2::GetSize)]
 | 
					#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
 | 
				
			||||||
pub struct GenericContextTypeVarOptions {
 | 
					pub struct GenericContextTypeVar<'db> {
 | 
				
			||||||
 | 
					    bound_typevar: BoundTypeVarInstance<'db>,
 | 
				
			||||||
    should_promote_literals: bool,
 | 
					    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 {
 | 
					    fn promote_literals(mut self) -> Self {
 | 
				
			||||||
        self.should_promote_literals = true;
 | 
					        self.should_promote_literals = true;
 | 
				
			||||||
        self
 | 
					        self
 | 
				
			||||||
| 
						 | 
					@ -160,7 +169,7 @@ impl GenericContextTypeVarOptions {
 | 
				
			||||||
#[derive(PartialOrd, Ord)]
 | 
					#[derive(PartialOrd, Ord)]
 | 
				
			||||||
pub struct GenericContext<'db> {
 | 
					pub struct GenericContext<'db> {
 | 
				
			||||||
    #[returns(ref)]
 | 
					    #[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>(
 | 
					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> {
 | 
					impl<'db> GenericContext<'db> {
 | 
				
			||||||
    fn from_variables(
 | 
					    fn from_variables(
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
        variables: impl IntoIterator<Item = (BoundTypeVarInstance<'db>, GenericContextTypeVarOptions)>,
 | 
					        variables: impl IntoIterator<Item = GenericContextTypeVar<'db>>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> 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.
 | 
					    /// Creates a generic context from a list of PEP-695 type parameters.
 | 
				
			||||||
| 
						 | 
					@ -203,12 +218,7 @@ impl<'db> GenericContext<'db> {
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
        type_params: impl IntoIterator<Item = BoundTypeVarInstance<'db>>,
 | 
					        type_params: impl IntoIterator<Item = BoundTypeVarInstance<'db>>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self::from_variables(
 | 
					        Self::from_variables(db, type_params.into_iter().map(GenericContextTypeVar::new))
 | 
				
			||||||
            db,
 | 
					 | 
				
			||||||
            type_params
 | 
					 | 
				
			||||||
                .into_iter()
 | 
					 | 
				
			||||||
                .map(|bound_typevar| (bound_typevar, GenericContextTypeVarOptions::default())),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns a copy of this generic context where we will promote literal types in any inferred
 | 
					    /// 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(
 | 
					        Self::from_variables(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            self.variables_inner(db)
 | 
					            self.variables_inner(db)
 | 
				
			||||||
                .iter()
 | 
					                .values()
 | 
				
			||||||
                .map(|(bound_typevar, options)| (*bound_typevar, options.promote_literals())),
 | 
					                .map(|variable| variable.promote_literals()),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -228,9 +238,9 @@ impl<'db> GenericContext<'db> {
 | 
				
			||||||
        Self::from_variables(
 | 
					        Self::from_variables(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            self.variables_inner(db)
 | 
					            self.variables_inner(db)
 | 
				
			||||||
                .iter()
 | 
					                .values()
 | 
				
			||||||
                .chain(other.variables_inner(db).iter())
 | 
					                .chain(other.variables_inner(db).values())
 | 
				
			||||||
                .map(|(bound_typevar, options)| (*bound_typevar, *options)),
 | 
					                .copied(),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -238,7 +248,9 @@ impl<'db> GenericContext<'db> {
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
    ) -> impl ExactSizeIterator<Item = BoundTypeVarInstance<'db>> + Clone {
 | 
					    ) -> 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(
 | 
					    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 {
 | 
					    pub(crate) fn is_subset_of(self, db: &'db dyn Db, other: GenericContext<'db>) -> bool {
 | 
				
			||||||
        let other_variables = other.variables_inner(db);
 | 
					        let other_variables = other.variables_inner(db);
 | 
				
			||||||
        self.variables(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(
 | 
					    pub(crate) fn binds_named_typevar(
 | 
				
			||||||
| 
						 | 
					@ -428,8 +440,9 @@ impl<'db> GenericContext<'db> {
 | 
				
			||||||
        db: &'db dyn Db,
 | 
					        db: &'db dyn Db,
 | 
				
			||||||
        typevar: TypeVarInstance<'db>,
 | 
					        typevar: TypeVarInstance<'db>,
 | 
				
			||||||
    ) -> Option<BoundTypeVarInstance<'db>> {
 | 
					    ) -> Option<BoundTypeVarInstance<'db>> {
 | 
				
			||||||
        self.variables(db)
 | 
					        self.variables(db).find(|self_bound_typevar| {
 | 
				
			||||||
            .find(|self_bound_typevar| self_bound_typevar.typevar(db).is_identical_to(db, 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
 | 
					    /// 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(
 | 
					    fn heap_size(
 | 
				
			||||||
        (variables,): &(FxOrderMap<BoundTypeVarInstance<'db>, GenericContextTypeVarOptions>,),
 | 
					        (variables,): &(FxOrderMap<BoundTypeVarIdentity<'db>, GenericContextTypeVar<'db>>,),
 | 
				
			||||||
    ) -> usize {
 | 
					    ) -> usize {
 | 
				
			||||||
        ruff_memory_usage::order_map_heap_size(variables)
 | 
					        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_variables = generic_context.variables(db);
 | 
				
			||||||
        let restricted_types: Option<Box<[_]>> = restricted_variables
 | 
					        let restricted_types: Option<Box<[_]>> = restricted_variables
 | 
				
			||||||
            .map(|variable| {
 | 
					            .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()
 | 
					                self_types.get(index).copied()
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .collect();
 | 
					            .collect();
 | 
				
			||||||
| 
						 | 
					@ -793,7 +806,7 @@ impl<'db> Specialization<'db> {
 | 
				
			||||||
        let index = self
 | 
					        let index = self
 | 
				
			||||||
            .generic_context(db)
 | 
					            .generic_context(db)
 | 
				
			||||||
            .variables_inner(db)
 | 
					            .variables_inner(db)
 | 
				
			||||||
            .get_index_of(&bound_typevar)?;
 | 
					            .get_index_of(&bound_typevar.identity(db))?;
 | 
				
			||||||
        self.types(db).get(index).copied()
 | 
					        self.types(db).get(index).copied()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1146,7 +1159,7 @@ impl<'db> PartialSpecialization<'_, 'db> {
 | 
				
			||||||
        let index = self
 | 
					        let index = self
 | 
				
			||||||
            .generic_context
 | 
					            .generic_context
 | 
				
			||||||
            .variables_inner(db)
 | 
					            .variables_inner(db)
 | 
				
			||||||
            .get_index_of(&bound_typevar)?;
 | 
					            .get_index_of(&bound_typevar.identity(db))?;
 | 
				
			||||||
        self.types.get(index).copied()
 | 
					        self.types.get(index).copied()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1155,7 +1168,7 @@ impl<'db> PartialSpecialization<'_, 'db> {
 | 
				
			||||||
/// specialization of a generic function.
 | 
					/// specialization of a generic function.
 | 
				
			||||||
pub(crate) struct SpecializationBuilder<'db> {
 | 
					pub(crate) struct SpecializationBuilder<'db> {
 | 
				
			||||||
    db: &'db dyn Db,
 | 
					    db: &'db dyn Db,
 | 
				
			||||||
    types: FxHashMap<BoundTypeVarInstance<'db>, Type<'db>>,
 | 
					    types: FxHashMap<BoundTypeVarIdentity<'db>, Type<'db>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'db> SpecializationBuilder<'db> {
 | 
					impl<'db> SpecializationBuilder<'db> {
 | 
				
			||||||
| 
						 | 
					@ -1175,15 +1188,16 @@ impl<'db> SpecializationBuilder<'db> {
 | 
				
			||||||
            .annotation
 | 
					            .annotation
 | 
				
			||||||
            .and_then(|annotation| annotation.specialization_of(self.db, None));
 | 
					            .and_then(|annotation| annotation.specialization_of(self.db, None));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let types = (generic_context.variables_inner(self.db).iter()).map(|(variable, options)| {
 | 
					        let types =
 | 
				
			||||||
            let mut ty = self.types.get(variable).copied();
 | 
					            (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,
 | 
					                // 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.
 | 
					                // promote any typevars that are inferred as a literal to the corresponding instance type.
 | 
				
			||||||
            if options.should_promote_literals {
 | 
					                if variable.should_promote_literals {
 | 
				
			||||||
                let tcx = tcx_specialization
 | 
					                    let tcx = tcx_specialization.and_then(|specialization| {
 | 
				
			||||||
                    .and_then(|specialization| specialization.get(self.db, *variable));
 | 
					                        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)));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1196,7 +1210,7 @@ impl<'db> SpecializationBuilder<'db> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
 | 
					    fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
 | 
				
			||||||
        self.types
 | 
					        self.types
 | 
				
			||||||
            .entry(bound_typevar)
 | 
					            .entry(bound_typevar.identity(self.db))
 | 
				
			||||||
            .and_modify(|existing| {
 | 
					            .and_modify(|existing| {
 | 
				
			||||||
                *existing = UnionType::from_elements(self.db, [*existing, ty]);
 | 
					                *existing = UnionType::from_elements(self.db, [*existing, ty]);
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,8 +94,9 @@ use crate::types::{
 | 
				
			||||||
    MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm,
 | 
					    MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm,
 | 
				
			||||||
    Parameters, SpecialFormType, SubclassOfType, TrackedConstraintSet, Truthiness, Type,
 | 
					    Parameters, SpecialFormType, SubclassOfType, TrackedConstraintSet, Truthiness, Type,
 | 
				
			||||||
    TypeAliasType, TypeAndQualifiers, TypeContext, TypeQualifiers,
 | 
					    TypeAliasType, TypeAndQualifiers, TypeContext, TypeQualifiers,
 | 
				
			||||||
    TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarInstance, TypeVarKind,
 | 
					    TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarIdentity,
 | 
				
			||||||
    TypeVarVariance, TypedDictType, UnionBuilder, UnionType, binding_type, todo_type,
 | 
					    TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder, UnionType,
 | 
				
			||||||
 | 
					    binding_type, todo_type,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
 | 
					use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
 | 
				
			||||||
use crate::unpack::{EvaluationMode, UnpackPosition};
 | 
					use crate::unpack::{EvaluationMode, UnpackPosition};
 | 
				
			||||||
| 
						 | 
					@ -3001,15 +3002,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
 | 
				
			||||||
        if bound_or_constraint.is_some() || default.is_some() {
 | 
					        if bound_or_constraint.is_some() || default.is_some() {
 | 
				
			||||||
            self.deferred.insert(definition);
 | 
					            self.deferred.insert(definition);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        let identity =
 | 
				
			||||||
 | 
					            TypeVarIdentity::new(self.db(), &name.id, Some(definition), TypeVarKind::Pep695);
 | 
				
			||||||
        let ty = Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
 | 
					        let ty = Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
 | 
				
			||||||
            self.db(),
 | 
					            self.db(),
 | 
				
			||||||
            &name.id,
 | 
					            identity,
 | 
				
			||||||
            Some(definition),
 | 
					 | 
				
			||||||
            bound_or_constraint,
 | 
					            bound_or_constraint,
 | 
				
			||||||
            None,
 | 
					            None, // explicit_variance
 | 
				
			||||||
            default.as_deref().map(|_| TypeVarDefaultEvaluation::Lazy),
 | 
					            default.as_deref().map(|_| TypeVarDefaultEvaluation::Lazy),
 | 
				
			||||||
            TypeVarKind::Pep695,
 | 
					 | 
				
			||||||
            None,
 | 
					 | 
				
			||||||
        )));
 | 
					        )));
 | 
				
			||||||
        self.add_declaration_with_binding(
 | 
					        self.add_declaration_with_binding(
 | 
				
			||||||
            node.into(),
 | 
					            node.into(),
 | 
				
			||||||
| 
						 | 
					@ -4340,15 +4340,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
 | 
				
			||||||
            self.deferred.insert(definition);
 | 
					            self.deferred.insert(definition);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let identity = TypeVarIdentity::new(db, target_name, Some(definition), TypeVarKind::Legacy);
 | 
				
			||||||
        Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
 | 
					        Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
 | 
				
			||||||
            db,
 | 
					            db,
 | 
				
			||||||
            target_name,
 | 
					            identity,
 | 
				
			||||||
            Some(definition),
 | 
					 | 
				
			||||||
            bound_or_constraints,
 | 
					            bound_or_constraints,
 | 
				
			||||||
            Some(variance),
 | 
					            Some(variance),
 | 
				
			||||||
            default,
 | 
					            default,
 | 
				
			||||||
            TypeVarKind::Legacy,
 | 
					 | 
				
			||||||
            None,
 | 
					 | 
				
			||||||
        )))
 | 
					        )))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue