mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] Track when type variables are inferable or not (#19786)
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 / 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 (push) Blocked by required conditions
CI / benchmarks-walltime (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 / 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 (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
`Type::TypeVar` now distinguishes whether the typevar in question is inferable or not. A typevar is _not inferable_ inside the body of the generic class or function that binds it: ```py def f[T](t: T) -> T: return t ``` The infered type of `t` in the function body is `TypeVar(T, NotInferable)`. This represents how e.g. assignability checks need to be valid for all possible specializations of the typevar. Most of the existing assignability/etc logic only applies to non-inferable typevars. Outside of the function body, the typevar is _inferable_: ```py f(4) ``` Here, the parameter type of `f` is `TypeVar(T, Inferable)`. This represents how e.g. assignability doesn't need to hold for _all_ specializations; instead, we need to find the constraints under which this specific assignability check holds. This is in support of starting to perform specialization inference _as part of_ performing the assignability check at the call site. In the [[POPL2015][]] paper, this concept is called _monomorphic_ / _polymorphic_, but I thought _non-inferable_ / _inferable_ would be clearer for us. Depends on #19784 [POPL2015]: https://doi.org/10.1145/2676726.2676991 --------- Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
parent
9ac39cee98
commit
b892e4548e
21 changed files with 383 additions and 158 deletions
|
@ -232,7 +232,7 @@ impl<'db> Completion<'db> {
|
|||
| Type::BytesLiteral(_) => CompletionKind::Value,
|
||||
Type::EnumLiteral(_) => CompletionKind::Enum,
|
||||
Type::ProtocolInstance(_) => CompletionKind::Interface,
|
||||
Type::TypeVar(_) => CompletionKind::TypeParameter,
|
||||
Type::NonInferableTypeVar(_) | Type::TypeVar(_) => CompletionKind::TypeParameter,
|
||||
Type::Union(union) => union.elements(db).iter().find_map(|&ty| imp(db, ty))?,
|
||||
Type::Intersection(intersection) => {
|
||||
intersection.iter_positive(db).find_map(|ty| imp(db, ty))?
|
||||
|
|
|
@ -44,7 +44,7 @@ use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
|||
use crate::types::diagnostic::{INVALID_AWAIT, INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||
use crate::types::enums::{enum_metadata, is_single_member_enum};
|
||||
use crate::types::function::{
|
||||
DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction,
|
||||
DataclassTransformerParams, FunctionDecorators, FunctionSpans, FunctionType, KnownFunction,
|
||||
};
|
||||
use crate::types::generics::{
|
||||
GenericContext, PartialSpecialization, Specialization, bind_typevar, walk_generic_context,
|
||||
|
@ -612,9 +612,15 @@ pub enum Type<'db> {
|
|||
LiteralString,
|
||||
/// A bytes literal
|
||||
BytesLiteral(BytesLiteralType<'db>),
|
||||
/// An instance of a typevar in a generic class or function. When the generic class or function
|
||||
/// is specialized, we will replace this typevar with its specialization.
|
||||
/// An instance of a typevar in a context where we can infer a specialization for it. (This is
|
||||
/// typically the signature of a generic function, or of a constructor of a generic class.)
|
||||
/// When the generic class or function binding this typevar is specialized, we will replace the
|
||||
/// typevar with its specialization.
|
||||
TypeVar(BoundTypeVarInstance<'db>),
|
||||
/// An instance of a typevar where we cannot infer a specialization for it. (This is typically
|
||||
/// the body of the generic function or class that binds the typevar.) In these positions,
|
||||
/// properties like assignability must hold for all possible specializations.
|
||||
NonInferableTypeVar(BoundTypeVarInstance<'db>),
|
||||
/// A bound super object like `super()` or `super(A, A())`
|
||||
/// This type doesn't handle an unbound super object like `super(A)`; for that we just use
|
||||
/// a `Type::NominalInstance` of `builtins.super`.
|
||||
|
@ -823,6 +829,9 @@ impl<'db> Type<'db> {
|
|||
)
|
||||
.build(),
|
||||
Type::TypeVar(bound_typevar) => Type::TypeVar(bound_typevar.materialize(db, variance)),
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar.materialize(db, variance))
|
||||
}
|
||||
Type::TypeIs(type_is) => {
|
||||
type_is.with_type(db, type_is.return_type(db).materialize(db, variance))
|
||||
}
|
||||
|
@ -1127,6 +1136,9 @@ impl<'db> Type<'db> {
|
|||
Type::TypeVar(bound_typevar) => visitor.visit(self, || {
|
||||
Type::TypeVar(bound_typevar.normalized_impl(db, visitor))
|
||||
}),
|
||||
Type::NonInferableTypeVar(bound_typevar) => visitor.visit(self, || {
|
||||
Type::NonInferableTypeVar(bound_typevar.normalized_impl(db, visitor))
|
||||
}),
|
||||
Type::KnownInstance(known_instance) => visitor.visit(self, || {
|
||||
Type::KnownInstance(known_instance.normalized_impl(db, visitor))
|
||||
}),
|
||||
|
@ -1203,6 +1215,7 @@ impl<'db> Type<'db> {
|
|||
| Type::Union(_)
|
||||
| Type::Intersection(_)
|
||||
| Type::Callable(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::TypeIs(_)
|
||||
|
@ -1283,6 +1296,7 @@ impl<'db> Type<'db> {
|
|||
| Type::KnownInstance(_)
|
||||
| Type::PropertyInstance(_)
|
||||
| Type::Intersection(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::BoundSuper(_) => None,
|
||||
}
|
||||
|
@ -1399,13 +1413,17 @@ impl<'db> Type<'db> {
|
|||
// However, there is one exception to this general rule: for any given typevar `T`,
|
||||
// `T` will always be a subtype of any union containing `T`.
|
||||
// A similar rule applies in reverse to intersection types.
|
||||
(Type::TypeVar(_), Type::Union(union)) if union.elements(db).contains(&self) => true,
|
||||
(Type::Intersection(intersection), Type::TypeVar(_))
|
||||
(Type::NonInferableTypeVar(_), Type::Union(union))
|
||||
if union.elements(db).contains(&self) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(Type::Intersection(intersection), Type::NonInferableTypeVar(_))
|
||||
if intersection.positive(db).contains(&target) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(Type::Intersection(intersection), Type::TypeVar(_))
|
||||
(Type::Intersection(intersection), Type::NonInferableTypeVar(_))
|
||||
if intersection.negative(db).contains(&target) =>
|
||||
{
|
||||
false
|
||||
|
@ -1416,16 +1434,15 @@ impl<'db> Type<'db> {
|
|||
//
|
||||
// Note that this is not handled by the early return at the beginning of this method,
|
||||
// since subtyping between a TypeVar and an arbitrary other type cannot be guaranteed to be reflexive.
|
||||
(Type::TypeVar(lhs_bound_typevar), Type::TypeVar(rhs_bound_typevar))
|
||||
if lhs_bound_typevar == rhs_bound_typevar =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(
|
||||
Type::NonInferableTypeVar(lhs_bound_typevar),
|
||||
Type::NonInferableTypeVar(rhs_bound_typevar),
|
||||
) if lhs_bound_typevar == rhs_bound_typevar => true,
|
||||
|
||||
// A fully static typevar is a subtype of its upper bound, and to something similar to
|
||||
// the union of its constraints. An unbound, unconstrained, fully static typevar has an
|
||||
// implicit upper bound of `object` (which is handled above).
|
||||
(Type::TypeVar(bound_typevar), _)
|
||||
(Type::NonInferableTypeVar(bound_typevar), _)
|
||||
if bound_typevar.typevar(db).bound_or_constraints(db).is_some() =>
|
||||
{
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
|
@ -1444,7 +1461,7 @@ impl<'db> Type<'db> {
|
|||
// If the typevar is constrained, there must be multiple constraints, and the typevar
|
||||
// might be specialized to any one of them. However, the constraints do not have to be
|
||||
// disjoint, which means an lhs type might be a subtype of all of the constraints.
|
||||
(_, Type::TypeVar(bound_typevar))
|
||||
(_, Type::NonInferableTypeVar(bound_typevar))
|
||||
if bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
|
@ -1496,7 +1513,10 @@ impl<'db> Type<'db> {
|
|||
// (If the typevar is bounded, it might be specialized to a smaller type than the
|
||||
// bound. This is true even if the bound is a final class, since the typevar can still
|
||||
// be specialized to `Never`.)
|
||||
(_, Type::TypeVar(_)) => false,
|
||||
(_, Type::NonInferableTypeVar(_)) => false,
|
||||
|
||||
// TODO: Infer specializations here
|
||||
(Type::TypeVar(_), _) | (_, Type::TypeVar(_)) => false,
|
||||
|
||||
// Note that the definition of `Type::AlwaysFalsy` depends on the return value of `__bool__`.
|
||||
// If `__bool__` always returns True or False, it can be treated as a subtype of `AlwaysTruthy` or `AlwaysFalsy`, respectively.
|
||||
|
@ -1756,7 +1776,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
// Other than the special cases enumerated above, `Instance` types and typevars are
|
||||
// never subtypes of any other variants
|
||||
(Type::NominalInstance(_) | Type::TypeVar(_), _) => false,
|
||||
(Type::NominalInstance(_) | Type::NonInferableTypeVar(_), _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1913,14 +1933,14 @@ impl<'db> Type<'db> {
|
|||
// be specialized to the same type. (This is an important difference between typevars
|
||||
// and `Any`!) Different typevars might be disjoint, depending on their bounds and
|
||||
// constraints, which are handled below.
|
||||
(Type::TypeVar(self_bound_typevar), Type::TypeVar(other_bound_typevar))
|
||||
(Type::NonInferableTypeVar(self_bound_typevar), Type::NonInferableTypeVar(other_bound_typevar))
|
||||
if self_bound_typevar == other_bound_typevar =>
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
(tvar @ Type::TypeVar(_), Type::Intersection(intersection))
|
||||
| (Type::Intersection(intersection), tvar @ Type::TypeVar(_))
|
||||
(tvar @ Type::NonInferableTypeVar(_), Type::Intersection(intersection))
|
||||
| (Type::Intersection(intersection), tvar @ Type::NonInferableTypeVar(_))
|
||||
if intersection.negative(db).contains(&tvar) =>
|
||||
{
|
||||
true
|
||||
|
@ -1930,7 +1950,7 @@ impl<'db> Type<'db> {
|
|||
// specialized to any type. A bounded typevar is not disjoint from its bound, and is
|
||||
// only disjoint from other types if its bound is. A constrained typevar is disjoint
|
||||
// from a type if all of its constraints are.
|
||||
(Type::TypeVar(bound_typevar), other) | (other, Type::TypeVar(bound_typevar)) => {
|
||||
(Type::NonInferableTypeVar(bound_typevar), other) | (other, Type::NonInferableTypeVar(bound_typevar)) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
|
@ -1943,6 +1963,10 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Infer specializations here
|
||||
(Type::TypeVar(_), _)
|
||||
| (_, Type::TypeVar(_)) => false,
|
||||
|
||||
(Type::Union(union), other) | (other, Type::Union(union)) => union
|
||||
.elements(db)
|
||||
.iter()
|
||||
|
@ -2383,7 +2407,7 @@ impl<'db> Type<'db> {
|
|||
// the bound is a final singleton class, since it can still be specialized to `Never`.
|
||||
// A constrained typevar is a singleton if all of its constraints are singletons. (Note
|
||||
// that you cannot specialize a constrained typevar to a subtype of a constraint.)
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(_)) => false,
|
||||
|
@ -2394,6 +2418,8 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Type::TypeVar(_) => false,
|
||||
|
||||
// We eagerly transform `SubclassOf` to `ClassLiteral` for final types, so `SubclassOf` is never a singleton.
|
||||
Type::SubclassOf(..) => false,
|
||||
Type::BoundSuper(..) => false,
|
||||
|
@ -2513,7 +2539,7 @@ impl<'db> Type<'db> {
|
|||
// `Never`. A constrained typevar is single-valued if all of its constraints are
|
||||
// single-valued. (Note that you cannot specialize a constrained typevar to a subtype
|
||||
// of a constraint.)
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => false,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(_)) => false,
|
||||
|
@ -2524,6 +2550,8 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Type::TypeVar(_) => false,
|
||||
|
||||
Type::SubclassOf(..) => {
|
||||
// TODO: Same comment as above for `is_singleton`
|
||||
false
|
||||
|
@ -2680,6 +2708,7 @@ impl<'db> Type<'db> {
|
|||
| Type::LiteralString
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::NominalInstance(_)
|
||||
| Type::ProtocolInstance(_)
|
||||
|
@ -2790,7 +2819,7 @@ impl<'db> Type<'db> {
|
|||
Type::object(db).instance_member(db, name)
|
||||
}
|
||||
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => Type::object(db).instance_member(db, name),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
|
@ -2803,6 +2832,16 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Type::TypeVar(_) => {
|
||||
debug_assert!(
|
||||
false,
|
||||
"should not be able to access instance member `{name}` \
|
||||
of type variable {} in inferable position",
|
||||
self.display(db)
|
||||
);
|
||||
Place::Unbound.into()
|
||||
}
|
||||
|
||||
Type::IntLiteral(_) => KnownClass::Int.to_instance(db).instance_member(db, name),
|
||||
Type::BooleanLiteral(_) | Type::TypeIs(_) => {
|
||||
KnownClass::Bool.to_instance(db).instance_member(db, name)
|
||||
|
@ -3329,6 +3368,7 @@ impl<'db> Type<'db> {
|
|||
| Type::BytesLiteral(..)
|
||||
| Type::EnumLiteral(..)
|
||||
| Type::LiteralString
|
||||
| Type::NonInferableTypeVar(..)
|
||||
| Type::TypeVar(..)
|
||||
| Type::SpecialForm(..)
|
||||
| Type::KnownInstance(..)
|
||||
|
@ -3664,7 +3704,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
},
|
||||
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => Truthiness::Ambiguous,
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
|
@ -3675,6 +3715,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Type::TypeVar(_) => Truthiness::Ambiguous,
|
||||
|
||||
Type::NominalInstance(instance) => instance
|
||||
.class(db)
|
||||
|
@ -3767,7 +3808,7 @@ impl<'db> Type<'db> {
|
|||
.into()
|
||||
}
|
||||
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => CallableBinding::not_callable(self).into(),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.bindings(db),
|
||||
|
@ -3780,6 +3821,15 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Type::TypeVar(_) => {
|
||||
debug_assert!(
|
||||
false,
|
||||
"should not be able to call type variable {} in inferable position",
|
||||
self.display(db)
|
||||
);
|
||||
CallableBinding::not_callable(self).into()
|
||||
}
|
||||
|
||||
Type::BoundMethod(bound_method) => {
|
||||
let signature = bound_method.function(db).signature(db);
|
||||
CallableBinding::from_overloads(self, signature.overloads.iter().cloned())
|
||||
|
@ -5111,20 +5161,22 @@ impl<'db> Type<'db> {
|
|||
// have the class's typevars still in the method signature when we attempt to call it. To
|
||||
// do this, we instead use the _identity_ specialization, which maps each of the class's
|
||||
// generic typevars to itself.
|
||||
let (generic_origin, generic_context, self_type) =
|
||||
match self {
|
||||
Type::ClassLiteral(class) => match class.generic_context(db) {
|
||||
Some(generic_context) => (
|
||||
Some(class),
|
||||
Some(generic_context),
|
||||
Type::from(class.apply_specialization(db, |_| {
|
||||
generic_context.identity_specialization(db)
|
||||
})),
|
||||
),
|
||||
_ => (None, None, self),
|
||||
},
|
||||
let (generic_origin, generic_context, self_type) = match self {
|
||||
Type::ClassLiteral(class) => match class.generic_context(db) {
|
||||
Some(generic_context) => (
|
||||
Some(class),
|
||||
Some(generic_context),
|
||||
Type::from(class.apply_specialization(db, |_| {
|
||||
// It is important that identity_specialization specializes the class with
|
||||
// _inferable_ typevars, so that our specialization inference logic will
|
||||
// try to find a specialization for them.
|
||||
generic_context.identity_specialization(db)
|
||||
})),
|
||||
),
|
||||
_ => (None, None, self),
|
||||
};
|
||||
},
|
||||
_ => (None, None, self),
|
||||
};
|
||||
|
||||
// As of now we do not model custom `__call__` on meta-classes, so the code below
|
||||
// only deals with interplay between `__new__` and `__init__` methods.
|
||||
|
@ -5281,32 +5333,10 @@ impl<'db> Type<'db> {
|
|||
// If there is no bound or constraints on a typevar `T`, `T: object` implicitly, which
|
||||
// has no instance type. Otherwise, synthesize a typevar with bound or constraints
|
||||
// mapped through `to_instance`.
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
let typevar = bound_typevar.typevar(db);
|
||||
let bound_or_constraints = match typevar.bound_or_constraints(db)? {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound) => {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound.to_instance(db)?)
|
||||
}
|
||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||
TypeVarBoundOrConstraints::Constraints(
|
||||
constraints.to_instance(db)?.into_union()?,
|
||||
)
|
||||
}
|
||||
};
|
||||
Some(Type::TypeVar(BoundTypeVarInstance::new(
|
||||
db,
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
Name::new(format!("{}'instance", typevar.name(db))),
|
||||
None,
|
||||
Some(bound_or_constraints.into()),
|
||||
typevar.variance(db),
|
||||
None,
|
||||
typevar.kind(db),
|
||||
),
|
||||
bound_typevar.binding_context(db),
|
||||
)))
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
Some(Type::NonInferableTypeVar(bound_typevar.to_instance(db)?))
|
||||
}
|
||||
Type::TypeVar(bound_typevar) => Some(Type::TypeVar(bound_typevar.to_instance(db)?)),
|
||||
Type::TypeAlias(alias) => alias.value_type(db).to_instance(db),
|
||||
Type::Intersection(_) => Some(todo_type!("Type::Intersection.to_instance")),
|
||||
Type::BooleanLiteral(_)
|
||||
|
@ -5390,6 +5420,7 @@ impl<'db> Type<'db> {
|
|||
| Type::LiteralString
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::StringLiteral(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::Callable(_)
|
||||
| Type::BoundMethod(_)
|
||||
|
@ -5423,7 +5454,7 @@ impl<'db> Type<'db> {
|
|||
typevar_binding_context,
|
||||
*typevar,
|
||||
)
|
||||
.map(Type::TypeVar)
|
||||
.map(Type::NonInferableTypeVar)
|
||||
.unwrap_or(*self))
|
||||
}
|
||||
KnownInstanceType::Deprecated(_) => Err(InvalidTypeExpressionError {
|
||||
|
@ -5506,7 +5537,7 @@ impl<'db> Type<'db> {
|
|||
Some(TypeVarBoundOrConstraints::UpperBound(instance).into()),
|
||||
TypeVarVariance::Invariant,
|
||||
None,
|
||||
TypeVarKind::Implicit,
|
||||
TypeVarKind::TypingSelf,
|
||||
);
|
||||
Ok(bind_typevar(
|
||||
db,
|
||||
|
@ -5516,7 +5547,7 @@ impl<'db> Type<'db> {
|
|||
typevar_binding_context,
|
||||
typevar,
|
||||
)
|
||||
.map(Type::TypeVar)
|
||||
.map(Type::NonInferableTypeVar)
|
||||
.unwrap_or(*self))
|
||||
}
|
||||
SpecialFormType::TypeAlias => Ok(Type::Dynamic(DynamicType::TodoTypeAlias)),
|
||||
|
@ -5685,7 +5716,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
Type::Callable(_) | Type::DataclassTransformer(_) => KnownClass::Type.to_instance(db),
|
||||
Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class_literal(db),
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => KnownClass::Type.to_instance(db),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.to_meta_type(db),
|
||||
|
@ -5696,6 +5727,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Type::TypeVar(_) => KnownClass::Type.to_instance(db),
|
||||
|
||||
Type::ClassLiteral(class) => class.metaclass(db),
|
||||
Type::GenericAlias(alias) => ClassType::from(*alias).metaclass(db),
|
||||
|
@ -5792,16 +5824,46 @@ impl<'db> Type<'db> {
|
|||
TypeMapping::PartialSpecialization(partial) => {
|
||||
partial.get(db, bound_typevar).unwrap_or(self)
|
||||
}
|
||||
TypeMapping::PromoteLiterals | TypeMapping::BindLegacyTypevars(_) => self,
|
||||
TypeMapping::BindSelf(self_type) => {
|
||||
if bound_typevar.typevar(db).is_self(db) {
|
||||
*self_type
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
TypeMapping::PromoteLiterals | TypeMapping::BindLegacyTypevars(_) |
|
||||
TypeMapping::MarkTypeVarsInferable(_) => self,
|
||||
}
|
||||
|
||||
Type::NonInferableTypeVar(bound_typevar) => match type_mapping {
|
||||
TypeMapping::Specialization(specialization) => {
|
||||
specialization.get(db, bound_typevar).unwrap_or(self)
|
||||
}
|
||||
TypeMapping::PartialSpecialization(partial) => {
|
||||
partial.get(db, bound_typevar).unwrap_or(self)
|
||||
}
|
||||
TypeMapping::MarkTypeVarsInferable(binding_context) => {
|
||||
if bound_typevar.binding_context(db) == *binding_context {
|
||||
Type::TypeVar(bound_typevar)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
TypeMapping::PromoteLiterals |
|
||||
TypeMapping::BindLegacyTypevars(_) |
|
||||
TypeMapping::BindSelf(_)
|
||||
=> self,
|
||||
}
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::PromoteLiterals => self,
|
||||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
|
||||
}
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::PromoteLiterals |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::MarkTypeVarsInferable(_) => self,
|
||||
}
|
||||
|
||||
Type::FunctionLiteral(function) => {
|
||||
|
@ -5896,7 +5958,9 @@ impl<'db> Type<'db> {
|
|||
| Type::EnumLiteral(_) => match type_mapping {
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::BindLegacyTypevars(_) => self,
|
||||
TypeMapping::BindLegacyTypevars(_) |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::MarkTypeVarsInferable(_) => self,
|
||||
TypeMapping::PromoteLiterals => self.literal_fallback_instance(db)
|
||||
.expect("literal type should have fallback instance type"),
|
||||
}
|
||||
|
@ -5929,10 +5993,10 @@ impl<'db> Type<'db> {
|
|||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
) {
|
||||
match self {
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
|
||||
if matches!(
|
||||
bound_typevar.typevar(db).kind(db),
|
||||
TypeVarKind::Legacy | TypeVarKind::Implicit
|
||||
TypeVarKind::Legacy | TypeVarKind::TypingSelf
|
||||
) && binding_context.is_none_or(|binding_context| {
|
||||
bound_typevar.binding_context(db) == BindingContext::Definition(binding_context)
|
||||
}) {
|
||||
|
@ -6145,6 +6209,7 @@ impl<'db> Type<'db> {
|
|||
| Self::PropertyInstance(_)
|
||||
| Self::BoundSuper(_) => self.to_meta_type(db).definition(db),
|
||||
|
||||
Self::NonInferableTypeVar(bound_typevar) |
|
||||
Self::TypeVar(bound_typevar) => Some(TypeDefinition::TypeVar(bound_typevar.typevar(db).definition(db)?)),
|
||||
|
||||
Self::ProtocolInstance(protocol) => match protocol.inner {
|
||||
|
@ -6270,6 +6335,10 @@ pub enum TypeMapping<'a, 'db> {
|
|||
/// Binds a legacy typevar with the generic context (class, function, type alias) that it is
|
||||
/// being used in.
|
||||
BindLegacyTypevars(BindingContext<'db>),
|
||||
/// Binds any `typing.Self` typevar with a particular `self` class.
|
||||
BindSelf(Type<'db>),
|
||||
/// Marks the typevars that are bound by a generic class or function as inferable.
|
||||
MarkTypeVarsInferable(BindingContext<'db>),
|
||||
}
|
||||
|
||||
fn walk_type_mapping<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
@ -6284,7 +6353,12 @@ fn walk_type_mapping<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
|||
TypeMapping::PartialSpecialization(specialization) => {
|
||||
walk_partial_specialization(db, specialization, visitor);
|
||||
}
|
||||
TypeMapping::PromoteLiterals | TypeMapping::BindLegacyTypevars(_) => {}
|
||||
TypeMapping::BindSelf(self_type) => {
|
||||
visitor.visit_type(db, *self_type);
|
||||
}
|
||||
TypeMapping::PromoteLiterals
|
||||
| TypeMapping::BindLegacyTypevars(_)
|
||||
| TypeMapping::MarkTypeVarsInferable(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6301,6 +6375,10 @@ impl<'db> TypeMapping<'_, 'db> {
|
|||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||
TypeMapping::BindLegacyTypevars(*binding_context)
|
||||
}
|
||||
TypeMapping::BindSelf(self_type) => TypeMapping::BindSelf(*self_type),
|
||||
TypeMapping::MarkTypeVarsInferable(binding_context) => {
|
||||
TypeMapping::MarkTypeVarsInferable(*binding_context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6316,6 +6394,12 @@ impl<'db> TypeMapping<'_, 'db> {
|
|||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||
TypeMapping::BindLegacyTypevars(*binding_context)
|
||||
}
|
||||
TypeMapping::BindSelf(self_type) => {
|
||||
TypeMapping::BindSelf(self_type.normalized_impl(db, visitor))
|
||||
}
|
||||
TypeMapping::MarkTypeVarsInferable(binding_context) => {
|
||||
TypeMapping::MarkTypeVarsInferable(*binding_context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6836,7 +6920,7 @@ pub enum TypeVarKind {
|
|||
/// `def foo[T](x: T) -> T: ...`
|
||||
Pep695,
|
||||
/// `typing.Self`
|
||||
Implicit,
|
||||
TypingSelf,
|
||||
}
|
||||
|
||||
/// A type variable that has not been bound to a generic context yet.
|
||||
|
@ -6926,8 +7010,8 @@ impl<'db> TypeVarInstance<'db> {
|
|||
BoundTypeVarInstance::new(db, self, BindingContext::Definition(binding_context))
|
||||
}
|
||||
|
||||
pub(crate) fn is_implicit(self, db: &'db dyn Db) -> bool {
|
||||
matches!(self.kind(db), TypeVarKind::Implicit)
|
||||
pub(crate) fn is_self(self, db: &'db dyn Db) -> bool {
|
||||
matches!(self.kind(db), TypeVarKind::TypingSelf)
|
||||
}
|
||||
|
||||
pub(crate) fn upper_bound(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
|
@ -7022,6 +7106,26 @@ impl<'db> TypeVarInstance<'db> {
|
|||
)
|
||||
}
|
||||
|
||||
fn to_instance(self, db: &'db dyn Db) -> Option<Self> {
|
||||
let bound_or_constraints = match self.bound_or_constraints(db)? {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound) => {
|
||||
TypeVarBoundOrConstraints::UpperBound(upper_bound.to_instance(db)?)
|
||||
}
|
||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||
TypeVarBoundOrConstraints::Constraints(constraints.to_instance(db)?.into_union()?)
|
||||
}
|
||||
};
|
||||
Some(Self::new(
|
||||
db,
|
||||
Name::new(format!("{}'instance", self.name(db))),
|
||||
None,
|
||||
Some(bound_or_constraints.into()),
|
||||
self.variance(db),
|
||||
None,
|
||||
self.kind(db),
|
||||
))
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn lazy_bound(self, db: &'db dyn Db) -> Option<TypeVarBoundOrConstraints<'db>> {
|
||||
let definition = self.definition(db)?;
|
||||
|
@ -7138,6 +7242,14 @@ impl<'db> BoundTypeVarInstance<'db> {
|
|||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_instance(self, db: &'db dyn Db) -> Option<Self> {
|
||||
Some(Self::new(
|
||||
db,
|
||||
self.typevar(db).to_instance(db)?,
|
||||
self.binding_context(db),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
|
@ -8328,16 +8440,35 @@ fn walk_bound_method_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
|||
visitor.visit_type(db, method.self_instance(db));
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
impl<'db> BoundMethodType<'db> {
|
||||
/// Returns the type that replaces any `typing.Self` annotations in the bound method signature.
|
||||
/// This is normally the bound-instance type (the type of `self` or `cls`), but if the bound method is
|
||||
/// a `@classmethod`, then it should be an instance of that bound-instance type.
|
||||
pub(crate) fn typing_self_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
let mut self_instance = self.self_instance(db);
|
||||
if self
|
||||
.function(db)
|
||||
.has_known_decorator(db, FunctionDecorators::CLASSMETHOD)
|
||||
{
|
||||
self_instance = self_instance.to_instance(db).unwrap_or_else(Type::unknown);
|
||||
}
|
||||
self_instance
|
||||
}
|
||||
|
||||
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> CallableType<'db> {
|
||||
let function = self.function(db);
|
||||
let self_instance = self.typing_self_type(db);
|
||||
|
||||
CallableType::new(
|
||||
db,
|
||||
CallableSignature::from_overloads(
|
||||
self.function(db)
|
||||
function
|
||||
.signature(db)
|
||||
.overloads
|
||||
.iter()
|
||||
.map(signatures::Signature::bind_self),
|
||||
.map(|signature| signature.bind_self(db, Some(self_instance))),
|
||||
),
|
||||
false,
|
||||
)
|
||||
|
@ -8435,7 +8566,7 @@ impl<'db> CallableType<'db> {
|
|||
pub(crate) fn bind_self(self, db: &'db dyn Db) -> Type<'db> {
|
||||
Type::Callable(CallableType::new(
|
||||
db,
|
||||
self.signatures(db).bind_self(),
|
||||
self.signatures(db).bind_self(db, None),
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -983,7 +983,8 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
let mut positive_to_remove = SmallVec::<[usize; 1]>::new();
|
||||
|
||||
for (typevar_index, ty) in self.positive.iter().enumerate() {
|
||||
let Type::TypeVar(bound_typevar) = ty else {
|
||||
let (Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar)) = ty
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(TypeVarBoundOrConstraints::Constraints(constraints)) =
|
||||
|
|
|
@ -966,6 +966,7 @@ impl<'db> ClassType<'db> {
|
|||
|
||||
/// Return a callable type (or union of callable types) that represents the callable
|
||||
/// constructor signature of this class.
|
||||
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(super) fn into_callable(self, db: &'db dyn Db) -> Type<'db> {
|
||||
let self_ty = Type::from(self);
|
||||
let metaclass_dunder_call_function_symbol = self_ty
|
||||
|
@ -1017,9 +1018,10 @@ impl<'db> ClassType<'db> {
|
|||
})
|
||||
});
|
||||
|
||||
let instance_ty = Type::instance(db, self);
|
||||
let dunder_new_bound_method = Type::Callable(CallableType::new(
|
||||
db,
|
||||
dunder_new_signature.bind_self(),
|
||||
dunder_new_signature.bind_self(db, Some(instance_ty)),
|
||||
true,
|
||||
));
|
||||
|
||||
|
@ -1057,9 +1059,10 @@ impl<'db> ClassType<'db> {
|
|||
|
||||
if let Some(signature) = signature {
|
||||
let synthesized_signature = |signature: &Signature<'db>| {
|
||||
let instance_ty = Type::instance(db, self);
|
||||
Signature::new(signature.parameters().clone(), Some(correct_return_type))
|
||||
.with_definition(signature.definition())
|
||||
.bind_self()
|
||||
.bind_self(db, Some(instance_ty))
|
||||
};
|
||||
|
||||
let synthesized_dunder_init_signature = CallableSignature::from_overloads(
|
||||
|
|
|
@ -155,6 +155,7 @@ impl<'db> ClassBase<'db> {
|
|||
| Type::StringLiteral(_)
|
||||
| Type::LiteralString
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::ProtocolInstance(_)
|
||||
|
|
|
@ -130,6 +130,8 @@ impl Display for DisplayRepresentation<'_> {
|
|||
Type::Callable(callable) => callable.display(self.db).fmt(f),
|
||||
Type::BoundMethod(bound_method) => {
|
||||
let function = bound_method.function(self.db);
|
||||
let self_ty = bound_method.self_instance(self.db);
|
||||
let typing_self_ty = bound_method.typing_self_type(self.db);
|
||||
|
||||
match function.signature(self.db).overloads.as_slice() {
|
||||
[signature] => {
|
||||
|
@ -142,9 +144,11 @@ impl Display for DisplayRepresentation<'_> {
|
|||
f,
|
||||
"bound method {instance}.{method}{type_parameters}{signature}",
|
||||
method = function.name(self.db),
|
||||
instance = bound_method.self_instance(self.db).display(self.db),
|
||||
instance = self_ty.display(self.db),
|
||||
type_parameters = type_parameters,
|
||||
signature = signature.bind_self().display(self.db)
|
||||
signature = signature
|
||||
.bind_self(self.db, Some(typing_self_ty))
|
||||
.display(self.db)
|
||||
)
|
||||
}
|
||||
signatures => {
|
||||
|
@ -152,7 +156,11 @@ impl Display for DisplayRepresentation<'_> {
|
|||
f.write_str("Overload[")?;
|
||||
let mut join = f.join(", ");
|
||||
for signature in signatures {
|
||||
join.entry(&signature.bind_self().display(self.db));
|
||||
join.entry(
|
||||
&signature
|
||||
.bind_self(self.db, Some(typing_self_ty))
|
||||
.display(self.db),
|
||||
);
|
||||
}
|
||||
f.write_str("]")
|
||||
}
|
||||
|
@ -214,7 +222,7 @@ impl Display for DisplayRepresentation<'_> {
|
|||
name = enum_literal.name(self.db),
|
||||
)
|
||||
}
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
|
||||
f.write_str(bound_typevar.typevar(self.db).name(self.db))?;
|
||||
if let Some(binding_context) = bound_typevar.binding_context(self.db).name(self.db)
|
||||
{
|
||||
|
@ -455,7 +463,7 @@ impl Display for DisplayGenericContext<'_> {
|
|||
|
||||
let non_implicit_variables: Vec<_> = variables
|
||||
.iter()
|
||||
.filter(|bound_typevar| !bound_typevar.typevar(self.db).is_implicit(self.db))
|
||||
.filter(|bound_typevar| !bound_typevar.typevar(self.db).is_self(self.db))
|
||||
.collect();
|
||||
|
||||
if non_implicit_variables.is_empty() {
|
||||
|
|
|
@ -1014,6 +1014,7 @@ fn is_instance_truthiness<'db>(
|
|||
| Type::PropertyInstance(..)
|
||||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| Type::NonInferableTypeVar(..)
|
||||
| Type::TypeVar(..)
|
||||
| Type::BoundSuper(..)
|
||||
| Type::TypeIs(..)
|
||||
|
|
|
@ -236,7 +236,8 @@ impl<'db> GenericContext<'db> {
|
|||
db: &'db dyn Db,
|
||||
known_class: Option<KnownClass>,
|
||||
) -> Specialization<'db> {
|
||||
let partial = self.specialize_partial(db, &vec![None; self.variables(db).len()]);
|
||||
let partial =
|
||||
self.specialize_partial(db, std::iter::repeat_n(None, self.variables(db).len()));
|
||||
if known_class == Some(KnownClass::Tuple) {
|
||||
Specialization::new(
|
||||
db,
|
||||
|
@ -249,6 +250,10 @@ impl<'db> GenericContext<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a specialization of this generic context where each typevar is mapped to itself.
|
||||
/// (And in particular, to an _inferable_ version of itself, since this will be used in our
|
||||
/// class constructor invocation machinery to infer a specialization for the class from the
|
||||
/// arguments passed to its constructor.)
|
||||
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||
let types = self
|
||||
.variables(db)
|
||||
|
@ -314,11 +319,12 @@ impl<'db> GenericContext<'db> {
|
|||
/// Creates a specialization of this generic context. Panics if the length of `types` does not
|
||||
/// match the number of typevars in the generic context. If any provided type is `None`, we
|
||||
/// will use the corresponding typevar's default type.
|
||||
pub(crate) fn specialize_partial(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
types: &[Option<Type<'db>>],
|
||||
) -> Specialization<'db> {
|
||||
pub(crate) fn specialize_partial<I>(self, db: &'db dyn Db, types: I) -> Specialization<'db>
|
||||
where
|
||||
I: IntoIterator<Item = Option<Type<'db>>>,
|
||||
I::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let types = types.into_iter();
|
||||
let variables = self.variables(db);
|
||||
assert!(variables.len() == types.len());
|
||||
|
||||
|
@ -331,9 +337,9 @@ impl<'db> GenericContext<'db> {
|
|||
// If there is a mapping for `T`, we want to map `U` to that type, not to `T`. To handle
|
||||
// this, we repeatedly apply the specialization to itself, until we reach a fixed point.
|
||||
let mut expanded = vec![Type::unknown(); types.len()];
|
||||
for (idx, (ty, typevar)) in types.iter().zip(variables).enumerate() {
|
||||
for (idx, (ty, typevar)) in types.zip(variables).enumerate() {
|
||||
if let Some(ty) = ty {
|
||||
expanded[idx] = *ty;
|
||||
expanded[idx] = ty;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -749,18 +755,12 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
}
|
||||
|
||||
pub(crate) fn build(&mut self, generic_context: GenericContext<'db>) -> Specialization<'db> {
|
||||
let types: Box<[_]> = generic_context
|
||||
let types = generic_context
|
||||
.variables(self.db)
|
||||
.iter()
|
||||
.map(|variable| {
|
||||
self.types
|
||||
.get(variable)
|
||||
.copied()
|
||||
.unwrap_or(variable.default_type(self.db).unwrap_or(Type::unknown()))
|
||||
})
|
||||
.collect();
|
||||
.map(|variable| self.types.get(variable).copied());
|
||||
// TODO Infer the tuple spec for a tuple type
|
||||
Specialization::new(self.db, generic_context, types, None)
|
||||
generic_context.specialize_partial(self.db, types)
|
||||
}
|
||||
|
||||
fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
|
||||
|
@ -777,6 +777,10 @@ impl<'db> SpecializationBuilder<'db> {
|
|||
formal: Type<'db>,
|
||||
actual: Type<'db>,
|
||||
) -> Result<(), SpecializationError<'db>> {
|
||||
if formal == actual {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If the actual type is a subtype of the formal type, then return without adding any new
|
||||
// type mappings. (Note that if the formal type contains any typevars, this check will
|
||||
// fail, since no non-typevar types are assignable to a typevar. Also note that we are
|
||||
|
|
|
@ -151,6 +151,7 @@ impl<'db> AllMembers<'db> {
|
|||
| Type::ProtocolInstance(_)
|
||||
| Type::SpecialForm(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::TypeIs(_) => match ty.to_meta_type(db) {
|
||||
|
|
|
@ -4025,6 +4025,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::WrapperDescriptor(_)
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::NonInferableTypeVar(..)
|
||||
| Type::TypeVar(..)
|
||||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
|
@ -7231,6 +7232,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::BytesLiteral(_)
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_),
|
||||
|
@ -7572,6 +7574,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::BytesLiteral(_)
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_),
|
||||
|
@ -7601,6 +7604,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::BytesLiteral(_)
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::NonInferableTypeVar(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_),
|
||||
|
@ -8652,7 +8656,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.next()
|
||||
.expect("valid bindings should have matching overload");
|
||||
Type::from(generic_class.apply_specialization(self.db(), |_| {
|
||||
generic_context.specialize_partial(self.db(), overload.parameter_types())
|
||||
generic_context
|
||||
.specialize_partial(self.db(), overload.parameter_types().iter().copied())
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -10604,7 +10609,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
|
||||
let mut signature_iter = callable_binding.into_iter().map(|binding| {
|
||||
if argument_type.is_bound_method() {
|
||||
binding.signature.bind_self()
|
||||
binding.signature.bind_self(self.db(), Some(argument_type))
|
||||
} else {
|
||||
binding.signature.clone()
|
||||
}
|
||||
|
|
|
@ -215,7 +215,7 @@ impl ClassInfoConstraintFunction {
|
|||
Type::Union(union) => {
|
||||
union.try_map(db, |element| self.generate_constraint(db, *element))
|
||||
}
|
||||
Type::TypeVar(bound_typevar) => match bound_typevar
|
||||
Type::NonInferableTypeVar(bound_typevar) => match bound_typevar
|
||||
.typevar(db)
|
||||
.bound_or_constraints(db)?
|
||||
{
|
||||
|
@ -259,6 +259,7 @@ impl ClassInfoConstraintFunction {
|
|||
| Type::IntLiteral(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::WrapperDescriptor(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::TypedDict(_) => None,
|
||||
|
|
|
@ -19,7 +19,8 @@ use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
|||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::generics::{GenericContext, walk_generic_context};
|
||||
use crate::types::{
|
||||
BoundTypeVarInstance, KnownClass, NormalizedVisitor, TypeMapping, TypeRelation, todo_type,
|
||||
BindingContext, BoundTypeVarInstance, KnownClass, NormalizedVisitor, TypeMapping, TypeRelation,
|
||||
todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
|
@ -98,9 +99,16 @@ impl<'db> CallableSignature<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bind_self(&self) -> Self {
|
||||
/// Binds the first (presumably `self`) parameter of this signature. If a `self_type` is
|
||||
/// provided, we will replace any occurrences of `typing.Self` in the parameter and return
|
||||
/// annotations with that type.
|
||||
pub(crate) fn bind_self(&self, db: &'db dyn Db, self_type: Option<Type<'db>>) -> Self {
|
||||
Self {
|
||||
overloads: self.overloads.iter().map(Signature::bind_self).collect(),
|
||||
overloads: self
|
||||
.overloads
|
||||
.iter()
|
||||
.map(|signature| signature.bind_self(db, self_type))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,8 +336,11 @@ impl<'db> Signature<'db> {
|
|||
let parameters =
|
||||
Parameters::from_parameters(db, definition, function_node.parameters.as_ref());
|
||||
let return_ty = function_node.returns.as_ref().map(|returns| {
|
||||
let plain_return_ty = definition_expression_type(db, definition, returns.as_ref());
|
||||
|
||||
let plain_return_ty = definition_expression_type(db, definition, returns.as_ref())
|
||||
.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::MarkTypeVarsInferable(BindingContext::Definition(definition)),
|
||||
);
|
||||
if function_node.is_async && !is_generator {
|
||||
KnownClass::CoroutineType
|
||||
.to_specialized_instance(db, [Type::any(), Type::any(), plain_return_ty])
|
||||
|
@ -457,13 +468,20 @@ impl<'db> Signature<'db> {
|
|||
self.definition
|
||||
}
|
||||
|
||||
pub(crate) fn bind_self(&self) -> Self {
|
||||
pub(crate) fn bind_self(&self, db: &'db dyn Db, self_type: Option<Type<'db>>) -> Self {
|
||||
let mut parameters = Parameters::new(self.parameters().iter().skip(1).cloned());
|
||||
let mut return_ty = self.return_ty;
|
||||
if let Some(self_type) = self_type {
|
||||
parameters = parameters.apply_type_mapping(db, &TypeMapping::BindSelf(self_type));
|
||||
return_ty =
|
||||
return_ty.map(|ty| ty.apply_type_mapping(db, &TypeMapping::BindSelf(self_type)));
|
||||
}
|
||||
Self {
|
||||
generic_context: self.generic_context,
|
||||
inherited_generic_context: self.inherited_generic_context,
|
||||
definition: self.definition,
|
||||
parameters: Parameters::new(self.parameters().iter().skip(1).cloned()),
|
||||
return_ty: self.return_ty,
|
||||
parameters,
|
||||
return_ty,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1432,9 +1450,12 @@ impl<'db> Parameter<'db> {
|
|||
kind: ParameterKind<'db>,
|
||||
) -> Self {
|
||||
Self {
|
||||
annotated_type: parameter
|
||||
.annotation()
|
||||
.map(|annotation| definition_expression_type(db, definition, annotation)),
|
||||
annotated_type: parameter.annotation().map(|annotation| {
|
||||
definition_expression_type(db, definition, annotation).apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::MarkTypeVarsInferable(BindingContext::Definition(definition)),
|
||||
)
|
||||
}),
|
||||
kind,
|
||||
form: ParameterForm::Value,
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ impl<'db> SubclassOfType<'db> {
|
|||
TypeVarVariance::Invariant => {
|
||||
// We need to materialize this to `type[T]` but that isn't representable so
|
||||
// we instead use a type variable with an upper bound of `type`.
|
||||
Type::TypeVar(BoundTypeVarInstance::new(
|
||||
Type::NonInferableTypeVar(BoundTypeVarInstance::new(
|
||||
db,
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
|
|
|
@ -142,6 +142,10 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
(Type::ProtocolInstance(_), _) => Ordering::Less,
|
||||
(_, Type::ProtocolInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::NonInferableTypeVar(left), Type::NonInferableTypeVar(right)) => left.cmp(right),
|
||||
(Type::NonInferableTypeVar(_), _) => Ordering::Less,
|
||||
(_, Type::NonInferableTypeVar(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypeVar(left), Type::TypeVar(right)) => left.cmp(right),
|
||||
(Type::TypeVar(_), _) => Ordering::Less,
|
||||
(_, Type::TypeVar(_)) => Ordering::Greater,
|
||||
|
|
|
@ -114,6 +114,7 @@ enum NonAtomicType<'db> {
|
|||
NominalInstance(NominalInstanceType<'db>),
|
||||
PropertyInstance(PropertyInstanceType<'db>),
|
||||
TypeIs(TypeIsType<'db>),
|
||||
NonInferableTypeVar(BoundTypeVarInstance<'db>),
|
||||
TypeVar(BoundTypeVarInstance<'db>),
|
||||
ProtocolInstance(ProtocolInstanceType<'db>),
|
||||
TypedDict(TypedDictType<'db>),
|
||||
|
@ -177,6 +178,9 @@ impl<'db> From<Type<'db>> for TypeKind<'db> {
|
|||
Type::PropertyInstance(property) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::PropertyInstance(property))
|
||||
}
|
||||
Type::NonInferableTypeVar(bound_typevar) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::NonInferableTypeVar(bound_typevar))
|
||||
}
|
||||
Type::TypeVar(bound_typevar) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::TypeVar(bound_typevar))
|
||||
}
|
||||
|
@ -216,6 +220,9 @@ fn walk_non_atomic_type<'db, V: TypeVisitor<'db> + ?Sized>(
|
|||
visitor.visit_property_instance_type(db, property);
|
||||
}
|
||||
NonAtomicType::TypeIs(type_is) => visitor.visit_typeis_type(db, type_is),
|
||||
NonAtomicType::NonInferableTypeVar(bound_typevar) => {
|
||||
visitor.visit_bound_type_var_type(db, bound_typevar);
|
||||
}
|
||||
NonAtomicType::TypeVar(bound_typevar) => {
|
||||
visitor.visit_bound_type_var_type(db, bound_typevar);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue