mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:28 +00:00
[ty] Promote literals when inferring class specializations from constructors (#18102)
This implements the stopgap approach described in https://github.com/astral-sh/ty/issues/336#issuecomment-2880532213 for handling literal types in generic class specializations. With this approach, we will promote any literal to its instance type, but _only_ when inferring a generic class specialization from a constructor call: ```py class C[T]: def __init__(self, x: T) -> None: ... reveal_type(C("string")) # revealed: C[str] ``` If you specialize the class explicitly, we still use whatever type you provide, even if it's a literal: ```py from typing import Literal reveal_type(C[Literal[5]](5)) # revealed: C[Literal[5]] ``` And this doesn't apply at all to generic functions: ```py def f[T](x: T) -> T: return x reveal_type(f(5)) # revealed: Literal[5] ``` --- As part of making this happen, we also generalize the `TypeMapping` machinery. This provides a way to apply a function to type, returning a new type. Complicating matters is that for function literals, we have to apply the mapping lazily, since the function's signature is not created until (and if) someone calls its `signature` method. That means we have to stash away the mappings that we want to apply to the signatures parameter/return annotations once we do create it. This requires some minor `Cow` shenanigans to continue working for partial specializations.
This commit is contained in:
parent
fb589730ef
commit
ce43dbab58
12 changed files with 215 additions and 176 deletions
|
@ -90,7 +90,7 @@ reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecializedExtraTyp
|
||||||
The type parameter can be specified explicitly:
|
The type parameter can be specified explicitly:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Generic, TypeVar
|
from typing import Generic, Literal, TypeVar
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ class C(Generic[T]):
|
||||||
x: T
|
x: T
|
||||||
|
|
||||||
reveal_type(C[int]()) # revealed: C[int]
|
reveal_type(C[int]()) # revealed: C[int]
|
||||||
|
reveal_type(C[Literal[5]]()) # revealed: C[Literal[5]]
|
||||||
```
|
```
|
||||||
|
|
||||||
The specialization must match the generic types:
|
The specialization must match the generic types:
|
||||||
|
@ -229,9 +230,9 @@ class C(Generic[T]):
|
||||||
def __new__(cls, x: T) -> "C[T]":
|
def __new__(cls, x: T) -> "C[T]":
|
||||||
return object.__new__(cls)
|
return object.__new__(cls)
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -245,9 +246,9 @@ T = TypeVar("T")
|
||||||
class C(Generic[T]):
|
class C(Generic[T]):
|
||||||
def __init__(self, x: T) -> None: ...
|
def __init__(self, x: T) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -264,9 +265,9 @@ class C(Generic[T]):
|
||||||
|
|
||||||
def __init__(self, x: T) -> None: ...
|
def __init__(self, x: T) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -283,9 +284,9 @@ class C(Generic[T]):
|
||||||
|
|
||||||
def __init__(self, x: T) -> None: ...
|
def __init__(self, x: T) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
|
|
||||||
class D(Generic[T]):
|
class D(Generic[T]):
|
||||||
|
@ -294,9 +295,9 @@ class D(Generic[T]):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> None: ...
|
def __init__(self, *args, **kwargs) -> None: ...
|
||||||
|
|
||||||
reveal_type(D(1)) # revealed: D[Literal[1]]
|
reveal_type(D(1)) # revealed: D[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `D[Literal["five"]]` is not assignable to `D[int]`"
|
# error: [invalid-assignment] "Object of type `D[str]` is not assignable to `D[int]`"
|
||||||
wrong_innards: D[int] = D("five")
|
wrong_innards: D[int] = D("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -319,7 +320,7 @@ class C(Generic[T, U]):
|
||||||
class D(C[V, int]):
|
class D(C[V, int]):
|
||||||
def __init__(self, x: V) -> None: ...
|
def __init__(self, x: V) -> None: ...
|
||||||
|
|
||||||
reveal_type(D(1)) # revealed: D[Literal[1]]
|
reveal_type(D(1)) # revealed: D[int]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `__init__` is itself generic
|
### `__init__` is itself generic
|
||||||
|
@ -333,11 +334,11 @@ T = TypeVar("T")
|
||||||
class C(Generic[T]):
|
class C(Generic[T]):
|
||||||
def __init__(self, x: T, y: S) -> None: ...
|
def __init__(self, x: T, y: S) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1, 1)) # revealed: C[Literal[1]]
|
reveal_type(C(1, 1)) # revealed: C[int]
|
||||||
reveal_type(C(1, "string")) # revealed: C[Literal[1]]
|
reveal_type(C(1, "string")) # revealed: C[int]
|
||||||
reveal_type(C(1, True)) # revealed: C[Literal[1]]
|
reveal_type(C(1, True)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five", 1)
|
wrong_innards: C[int] = C("five", 1)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -74,10 +74,13 @@ class BothGenericSyntaxes[U](Generic[T]): ...
|
||||||
The type parameter can be specified explicitly:
|
The type parameter can be specified explicitly:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
class C[T]:
|
class C[T]:
|
||||||
x: T
|
x: T
|
||||||
|
|
||||||
reveal_type(C[int]()) # revealed: C[int]
|
reveal_type(C[int]()) # revealed: C[int]
|
||||||
|
reveal_type(C[Literal[5]]()) # revealed: C[Literal[5]]
|
||||||
```
|
```
|
||||||
|
|
||||||
The specialization must match the generic types:
|
The specialization must match the generic types:
|
||||||
|
@ -190,9 +193,9 @@ class C[T]:
|
||||||
def __new__(cls, x: T) -> "C[T]":
|
def __new__(cls, x: T) -> "C[T]":
|
||||||
return object.__new__(cls)
|
return object.__new__(cls)
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -202,9 +205,9 @@ wrong_innards: C[int] = C("five")
|
||||||
class C[T]:
|
class C[T]:
|
||||||
def __init__(self, x: T) -> None: ...
|
def __init__(self, x: T) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -217,9 +220,9 @@ class C[T]:
|
||||||
|
|
||||||
def __init__(self, x: T) -> None: ...
|
def __init__(self, x: T) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -232,9 +235,9 @@ class C[T]:
|
||||||
|
|
||||||
def __init__(self, x: T) -> None: ...
|
def __init__(self, x: T) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1)) # revealed: C[Literal[1]]
|
reveal_type(C(1)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five")
|
wrong_innards: C[int] = C("five")
|
||||||
|
|
||||||
class D[T]:
|
class D[T]:
|
||||||
|
@ -243,9 +246,9 @@ class D[T]:
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> None: ...
|
def __init__(self, *args, **kwargs) -> None: ...
|
||||||
|
|
||||||
reveal_type(D(1)) # revealed: D[Literal[1]]
|
reveal_type(D(1)) # revealed: D[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `D[Literal["five"]]` is not assignable to `D[int]`"
|
# error: [invalid-assignment] "Object of type `D[str]` is not assignable to `D[int]`"
|
||||||
wrong_innards: D[int] = D("five")
|
wrong_innards: D[int] = D("five")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -262,7 +265,7 @@ class C[T, U]:
|
||||||
class D[V](C[V, int]):
|
class D[V](C[V, int]):
|
||||||
def __init__(self, x: V) -> None: ...
|
def __init__(self, x: V) -> None: ...
|
||||||
|
|
||||||
reveal_type(D(1)) # revealed: D[Literal[1]]
|
reveal_type(D(1)) # revealed: D[int]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `__init__` is itself generic
|
### `__init__` is itself generic
|
||||||
|
@ -271,11 +274,11 @@ reveal_type(D(1)) # revealed: D[Literal[1]]
|
||||||
class C[T]:
|
class C[T]:
|
||||||
def __init__[S](self, x: T, y: S) -> None: ...
|
def __init__[S](self, x: T, y: S) -> None: ...
|
||||||
|
|
||||||
reveal_type(C(1, 1)) # revealed: C[Literal[1]]
|
reveal_type(C(1, 1)) # revealed: C[int]
|
||||||
reveal_type(C(1, "string")) # revealed: C[Literal[1]]
|
reveal_type(C(1, "string")) # revealed: C[int]
|
||||||
reveal_type(C(1, True)) # revealed: C[Literal[1]]
|
reveal_type(C(1, True)) # revealed: C[int]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `C[Literal["five"]]` is not assignable to `C[int]`"
|
# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`"
|
||||||
wrong_innards: C[int] = C("five", 1)
|
wrong_innards: C[int] = C("five", 1)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ use crate::types::call::{Bindings, CallArgumentTypes, CallableBinding};
|
||||||
pub(crate) use crate::types::class_base::ClassBase;
|
pub(crate) use crate::types::class_base::ClassBase;
|
||||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||||
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||||
use crate::types::generics::{GenericContext, Specialization, TypeMapping};
|
use crate::types::generics::{GenericContext, PartialSpecialization, Specialization};
|
||||||
use crate::types::infer::infer_unpack_types;
|
use crate::types::infer::infer_unpack_types;
|
||||||
use crate::types::mro::{Mro, MroError, MroIterator};
|
use crate::types::mro::{Mro, MroError, MroIterator};
|
||||||
pub(crate) use crate::types::narrow::infer_narrowing_constraint;
|
pub(crate) use crate::types::narrow::infer_narrowing_constraint;
|
||||||
|
@ -342,7 +342,7 @@ pub struct PropertyInstanceType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> PropertyInstanceType<'db> {
|
impl<'db> PropertyInstanceType<'db> {
|
||||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
let getter = self
|
let getter = self
|
||||||
.getter(db)
|
.getter(db)
|
||||||
.map(|ty| ty.apply_type_mapping(db, type_mapping));
|
.map(|ty| ty.apply_type_mapping(db, type_mapping));
|
||||||
|
@ -963,6 +963,23 @@ impl<'db> Type<'db> {
|
||||||
if yes { self.negate(db) } else { *self }
|
if yes { self.negate(db) } else { *self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the fallback instance type that a literal is an instance of, or `None` if the type
|
||||||
|
/// is not a literal.
|
||||||
|
pub fn literal_fallback_instance(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||||
|
// There are other literal types that could conceivable be included here: class literals
|
||||||
|
// falling back to `type[X]`, for instance. For now, there is not much rigorous thought put
|
||||||
|
// into what's included vs not; this is just an empirical choice that makes our ecosystem
|
||||||
|
// report look better until we have proper bidirectional type inference.
|
||||||
|
match self {
|
||||||
|
Type::StringLiteral(_) | Type::LiteralString => Some(KnownClass::Str.to_instance(db)),
|
||||||
|
Type::BooleanLiteral(_) => Some(KnownClass::Bool.to_instance(db)),
|
||||||
|
Type::IntLiteral(_) => Some(KnownClass::Int.to_instance(db)),
|
||||||
|
Type::BytesLiteral(_) => Some(KnownClass::Bytes.to_instance(db)),
|
||||||
|
Type::ModuleLiteral(_) => Some(KnownClass::ModuleType.to_instance(db)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a "normalized" version of `self` that ensures that equivalent types have the same Salsa ID.
|
/// Return a "normalized" version of `self` that ensures that equivalent types have the same Salsa ID.
|
||||||
///
|
///
|
||||||
/// A normalized type:
|
/// A normalized type:
|
||||||
|
@ -1210,19 +1227,16 @@ impl<'db> Type<'db> {
|
||||||
// Except for the special `LiteralString` case above,
|
// Except for the special `LiteralString` case above,
|
||||||
// most `Literal` types delegate to their instance fallbacks
|
// most `Literal` types delegate to their instance fallbacks
|
||||||
// unless `self` is exactly equivalent to `target` (handled above)
|
// unless `self` is exactly equivalent to `target` (handled above)
|
||||||
(Type::StringLiteral(_) | Type::LiteralString, _) => {
|
(
|
||||||
KnownClass::Str.to_instance(db).is_subtype_of(db, target)
|
Type::StringLiteral(_)
|
||||||
}
|
| Type::LiteralString
|
||||||
(Type::BooleanLiteral(_), _) => {
|
| Type::BooleanLiteral(_)
|
||||||
KnownClass::Bool.to_instance(db).is_subtype_of(db, target)
|
| Type::IntLiteral(_)
|
||||||
}
|
| Type::BytesLiteral(_)
|
||||||
(Type::IntLiteral(_), _) => KnownClass::Int.to_instance(db).is_subtype_of(db, target),
|
| Type::ModuleLiteral(_),
|
||||||
(Type::BytesLiteral(_), _) => {
|
_,
|
||||||
KnownClass::Bytes.to_instance(db).is_subtype_of(db, target)
|
) => (self.literal_fallback_instance(db))
|
||||||
}
|
.is_some_and(|instance| instance.is_subtype_of(db, target)),
|
||||||
(Type::ModuleLiteral(_), _) => KnownClass::ModuleType
|
|
||||||
.to_instance(db)
|
|
||||||
.is_subtype_of(db, target),
|
|
||||||
|
|
||||||
(Type::FunctionLiteral(self_function_literal), Type::Callable(_)) => {
|
(Type::FunctionLiteral(self_function_literal), Type::Callable(_)) => {
|
||||||
self_function_literal
|
self_function_literal
|
||||||
|
@ -5124,24 +5138,32 @@ impl<'db> Type<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
specialization: Specialization<'db>,
|
specialization: Specialization<'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
self.apply_type_mapping(db, specialization.type_mapping())
|
self.apply_type_mapping(db, &TypeMapping::Specialization(specialization))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(
|
fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
match self {
|
match self {
|
||||||
Type::TypeVar(typevar) => type_mapping.get(db, typevar).unwrap_or(self),
|
Type::TypeVar(typevar) => match type_mapping {
|
||||||
|
TypeMapping::Specialization(specialization) => {
|
||||||
|
specialization.get(db, typevar).unwrap_or(self)
|
||||||
|
}
|
||||||
|
TypeMapping::PartialSpecialization(partial) => {
|
||||||
|
partial.get(db, typevar).unwrap_or(self)
|
||||||
|
}
|
||||||
|
TypeMapping::PromoteLiterals => self,
|
||||||
|
}
|
||||||
|
|
||||||
Type::FunctionLiteral(function) => {
|
Type::FunctionLiteral(function) => {
|
||||||
Type::FunctionLiteral(function.apply_type_mapping(db, type_mapping))
|
Type::FunctionLiteral(function.with_type_mapping(db, type_mapping))
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::BoundMethod(method) => Type::BoundMethod(BoundMethodType::new(
|
Type::BoundMethod(method) => Type::BoundMethod(BoundMethodType::new(
|
||||||
db,
|
db,
|
||||||
method.function(db).apply_type_mapping(db, type_mapping),
|
method.function(db).with_type_mapping(db, type_mapping),
|
||||||
method.self_instance(db).apply_type_mapping(db, type_mapping),
|
method.self_instance(db).apply_type_mapping(db, type_mapping),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
|
@ -5155,13 +5177,13 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
||||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(
|
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(
|
||||||
function.apply_type_mapping(db, type_mapping),
|
function.with_type_mapping(db, type_mapping),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderCall(function)) => {
|
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderCall(function)) => {
|
||||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderCall(
|
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderCall(
|
||||||
function.apply_type_mapping(db, type_mapping),
|
function.with_type_mapping(db, type_mapping),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5215,6 +5237,18 @@ impl<'db> Type<'db> {
|
||||||
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
|
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Type::ModuleLiteral(_)
|
||||||
|
| Type::IntLiteral(_)
|
||||||
|
| Type::BooleanLiteral(_)
|
||||||
|
| Type::LiteralString
|
||||||
|
| Type::StringLiteral(_)
|
||||||
|
| Type::BytesLiteral(_) => match type_mapping {
|
||||||
|
TypeMapping::Specialization(_) |
|
||||||
|
TypeMapping::PartialSpecialization(_) => self,
|
||||||
|
TypeMapping::PromoteLiterals => self.literal_fallback_instance(db)
|
||||||
|
.expect("literal type should have fallback instance type"),
|
||||||
|
}
|
||||||
|
|
||||||
Type::Dynamic(_)
|
Type::Dynamic(_)
|
||||||
| Type::Never
|
| Type::Never
|
||||||
| Type::AlwaysTruthy
|
| Type::AlwaysTruthy
|
||||||
|
@ -5223,16 +5257,10 @@ impl<'db> Type<'db> {
|
||||||
| Type::MethodWrapper(MethodWrapperKind::StrStartswith(_))
|
| Type::MethodWrapper(MethodWrapperKind::StrStartswith(_))
|
||||||
| Type::DataclassDecorator(_)
|
| Type::DataclassDecorator(_)
|
||||||
| Type::DataclassTransformer(_)
|
| Type::DataclassTransformer(_)
|
||||||
| Type::ModuleLiteral(_)
|
|
||||||
// A non-generic class never needs to be specialized. A generic class is specialized
|
// A non-generic class never needs to be specialized. A generic class is specialized
|
||||||
// explicitly (via a subscript expression) or implicitly (via a call), and not because
|
// explicitly (via a subscript expression) or implicitly (via a call), and not because
|
||||||
// some other generic context's specialization is applied to it.
|
// some other generic context's specialization is applied to it.
|
||||||
| Type::ClassLiteral(_)
|
| Type::ClassLiteral(_)
|
||||||
| Type::IntLiteral(_)
|
|
||||||
| Type::BooleanLiteral(_)
|
|
||||||
| Type::LiteralString
|
|
||||||
| Type::StringLiteral(_)
|
|
||||||
| Type::BytesLiteral(_)
|
|
||||||
| Type::BoundSuper(_)
|
| Type::BoundSuper(_)
|
||||||
| Type::KnownInstance(_) => self,
|
| Type::KnownInstance(_) => self,
|
||||||
}
|
}
|
||||||
|
@ -5516,6 +5544,37 @@ impl<'db> From<&Type<'db>> for Type<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mapping that can be applied to a type, producing another type. This is applied inductively to
|
||||||
|
/// the components of complex types.
|
||||||
|
///
|
||||||
|
/// This is represented as an enum (with some variants using `Cow`), and not an `FnMut` trait,
|
||||||
|
/// since we sometimes have to apply type mappings lazily (e.g., to the signature of a function
|
||||||
|
/// literal).
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum TypeMapping<'a, 'db> {
|
||||||
|
/// Applies a specialization to the type
|
||||||
|
Specialization(Specialization<'db>),
|
||||||
|
/// Applies a partial specialization to the type
|
||||||
|
PartialSpecialization(PartialSpecialization<'a, 'db>),
|
||||||
|
/// Promotes any literal types to their corresponding instance types (e.g. `Literal["string"]`
|
||||||
|
/// to `str`)
|
||||||
|
PromoteLiterals,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> TypeMapping<'_, 'db> {
|
||||||
|
fn to_owned(&self) -> TypeMapping<'db, 'db> {
|
||||||
|
match self {
|
||||||
|
TypeMapping::Specialization(specialization) => {
|
||||||
|
TypeMapping::Specialization(*specialization)
|
||||||
|
}
|
||||||
|
TypeMapping::PartialSpecialization(partial) => {
|
||||||
|
TypeMapping::PartialSpecialization(partial.to_owned())
|
||||||
|
}
|
||||||
|
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub enum DynamicType {
|
pub enum DynamicType {
|
||||||
// An explicitly annotated `typing.Any`
|
// An explicitly annotated `typing.Any`
|
||||||
|
@ -6685,10 +6744,8 @@ pub struct FunctionType<'db> {
|
||||||
/// to its own generic context.
|
/// to its own generic context.
|
||||||
inherited_generic_context: Option<GenericContext<'db>>,
|
inherited_generic_context: Option<GenericContext<'db>>,
|
||||||
|
|
||||||
/// A specialization that should be applied to the function's parameter and return types,
|
/// Type mappings that should be applied to the function's parameter and return types.
|
||||||
/// either because the function is itself generic, or because it appears in the body of a
|
type_mappings: Box<[TypeMapping<'db, 'db>]>,
|
||||||
/// generic class.
|
|
||||||
specialization: Option<Specialization<'db>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::tracked]
|
#[salsa::tracked]
|
||||||
|
@ -6777,29 +6834,33 @@ impl<'db> FunctionType<'db> {
|
||||||
#[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial)]
|
#[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial)]
|
||||||
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
||||||
let inherited_generic_context = self.inherited_generic_context(db);
|
let inherited_generic_context = self.inherited_generic_context(db);
|
||||||
let specialization = self.specialization(db);
|
let type_mappings = self.type_mappings(db);
|
||||||
if let Some(overloaded) = self.to_overloaded(db) {
|
if let Some(overloaded) = self.to_overloaded(db) {
|
||||||
FunctionSignature {
|
FunctionSignature {
|
||||||
overloads: CallableSignature::from_overloads(
|
overloads: CallableSignature::from_overloads(
|
||||||
Type::FunctionLiteral(self),
|
Type::FunctionLiteral(self),
|
||||||
overloaded.overloads.iter().copied().map(|overload| {
|
overloaded.overloads.iter().copied().map(|overload| {
|
||||||
overload
|
type_mappings.iter().fold(
|
||||||
.internal_signature(db, inherited_generic_context)
|
overload.internal_signature(db, inherited_generic_context),
|
||||||
.apply_optional_specialization(db, specialization)
|
|ty, mapping| ty.apply_type_mapping(db, mapping),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
implementation: overloaded.implementation.map(|implementation| {
|
implementation: overloaded.implementation.map(|implementation| {
|
||||||
implementation
|
type_mappings.iter().fold(
|
||||||
.internal_signature(db, inherited_generic_context)
|
implementation.internal_signature(db, inherited_generic_context),
|
||||||
.apply_optional_specialization(db, specialization)
|
|ty, mapping| ty.apply_type_mapping(db, mapping),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FunctionSignature {
|
FunctionSignature {
|
||||||
overloads: CallableSignature::single(
|
overloads: CallableSignature::single(
|
||||||
Type::FunctionLiteral(self),
|
Type::FunctionLiteral(self),
|
||||||
self.internal_signature(db, inherited_generic_context)
|
type_mappings.iter().fold(
|
||||||
.apply_optional_specialization(db, specialization),
|
self.internal_signature(db, inherited_generic_context),
|
||||||
|
|ty, mapping| ty.apply_type_mapping(db, mapping),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
implementation: None,
|
implementation: None,
|
||||||
}
|
}
|
||||||
|
@ -6854,7 +6915,7 @@ impl<'db> FunctionType<'db> {
|
||||||
self.decorators(db),
|
self.decorators(db),
|
||||||
Some(params),
|
Some(params),
|
||||||
self.inherited_generic_context(db),
|
self.inherited_generic_context(db),
|
||||||
self.specialization(db),
|
self.type_mappings(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6873,15 +6934,17 @@ impl<'db> FunctionType<'db> {
|
||||||
self.decorators(db),
|
self.decorators(db),
|
||||||
self.dataclass_transformer_params(db),
|
self.dataclass_transformer_params(db),
|
||||||
Some(inherited_generic_context),
|
Some(inherited_generic_context),
|
||||||
self.specialization(db),
|
self.type_mappings(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_specialization(self, db: &'db dyn Db, specialization: Specialization<'db>) -> Self {
|
fn with_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
let specialization = match self.specialization(db) {
|
let type_mappings: Box<[_]> = self
|
||||||
Some(existing) => existing.apply_specialization(db, specialization),
|
.type_mappings(db)
|
||||||
None => specialization,
|
.iter()
|
||||||
};
|
.cloned()
|
||||||
|
.chain(std::iter::once(type_mapping.to_owned()))
|
||||||
|
.collect();
|
||||||
Self::new(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.name(db).clone(),
|
self.name(db).clone(),
|
||||||
|
@ -6890,14 +6953,10 @@ impl<'db> FunctionType<'db> {
|
||||||
self.decorators(db),
|
self.decorators(db),
|
||||||
self.dataclass_transformer_params(db),
|
self.dataclass_transformer_params(db),
|
||||||
self.inherited_generic_context(db),
|
self.inherited_generic_context(db),
|
||||||
Some(specialization),
|
type_mappings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
|
||||||
self.apply_specialization(db, type_mapping.into_specialization(db))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_legacy_typevars(
|
fn find_legacy_typevars(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
@ -7408,7 +7467,7 @@ impl<'db> CallableType<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
CallableType::from_overloads(
|
CallableType::from_overloads(
|
||||||
db,
|
db,
|
||||||
self.signatures(db)
|
self.signatures(db)
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::types::signatures::{Parameter, ParameterForm};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundMethodType, DataclassParams, DataclassTransformerParams, FunctionDecorators, FunctionType,
|
BoundMethodType, DataclassParams, DataclassTransformerParams, FunctionDecorators, FunctionType,
|
||||||
KnownClass, KnownFunction, KnownInstanceType, MethodWrapperKind, PropertyInstanceType,
|
KnownClass, KnownFunction, KnownInstanceType, MethodWrapperKind, PropertyInstanceType,
|
||||||
TupleType, UnionType, WrapperDescriptorKind, todo_type,
|
TupleType, TypeMapping, UnionType, WrapperDescriptorKind, todo_type,
|
||||||
};
|
};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
@ -1406,9 +1406,14 @@ impl<'db> Binding<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.specialization = signature.generic_context.map(|gc| builder.build(gc));
|
self.specialization = signature.generic_context.map(|gc| builder.build(gc));
|
||||||
self.inherited_specialization = signature
|
self.inherited_specialization = signature.inherited_generic_context.map(|gc| {
|
||||||
.inherited_generic_context
|
// The inherited generic context is used when inferring the specialization of a
|
||||||
.map(|gc| builder.build(gc));
|
// generic class from a constructor call. In this case (only), we promote any
|
||||||
|
// typevars that are inferred as a literal to the corresponding instance type.
|
||||||
|
builder
|
||||||
|
.build(gc)
|
||||||
|
.apply_type_mapping(db, &TypeMapping::PromoteLiterals)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
num_synthetic_args = 0;
|
num_synthetic_args = 0;
|
||||||
|
|
|
@ -8,11 +8,11 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::semantic_index::DeclarationWithConstraint;
|
use crate::semantic_index::DeclarationWithConstraint;
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::types::generics::{GenericContext, Specialization, TypeMapping};
|
use crate::types::generics::{GenericContext, Specialization};
|
||||||
use crate::types::signatures::{Parameter, Parameters};
|
use crate::types::signatures::{Parameter, Parameters};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
CallableType, DataclassParams, DataclassTransformerParams, KnownInstanceType, Signature,
|
CallableType, DataclassParams, DataclassTransformerParams, KnownInstanceType, Signature,
|
||||||
TypeVarInstance,
|
TypeMapping, TypeVarInstance,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxOrderSet, KnownModule, Program,
|
Db, FxOrderSet, KnownModule, Program,
|
||||||
|
@ -175,7 +175,7 @@ impl<'db> GenericAlias<'db> {
|
||||||
pub(super) fn apply_type_mapping<'a>(
|
pub(super) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
|
@ -278,7 +278,7 @@ impl<'db> ClassType<'db> {
|
||||||
pub(super) fn apply_type_mapping<'a>(
|
pub(super) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::NonGeneric(_) => self,
|
Self::NonGeneric(_) => self,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::types::generics::{GenericContext, Specialization, TypeMapping};
|
use crate::types::generics::{GenericContext, Specialization};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ClassType, DynamicType, KnownClass, KnownInstanceType, MroError, MroIterator, Type, todo_type,
|
ClassType, DynamicType, KnownClass, KnownInstanceType, MroError, MroIterator, Type,
|
||||||
|
TypeMapping, todo_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Enumeration of the possible kinds of types we allow in class bases.
|
/// Enumeration of the possible kinds of types we allow in class bases.
|
||||||
|
@ -254,7 +255,7 @@ impl<'db> ClassBase<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Class(class) => Self::Class(class.apply_type_mapping(db, type_mapping)),
|
Self::Class(class) => Self::Class(class.apply_type_mapping(db, type_mapping)),
|
||||||
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol(_) => self,
|
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol(_) => self,
|
||||||
|
@ -267,7 +268,7 @@ impl<'db> ClassBase<'db> {
|
||||||
specialization: Option<Specialization<'db>>,
|
specialization: Option<Specialization<'db>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if let Some(specialization) = specialization {
|
if let Some(specialization) = specialization {
|
||||||
self.apply_type_mapping(db, specialization.type_mapping())
|
self.apply_type_mapping(db, &TypeMapping::Specialization(specialization))
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
@ -7,8 +9,8 @@ use crate::types::class_base::ClassBase;
|
||||||
use crate::types::instance::{NominalInstanceType, Protocol, ProtocolInstanceType};
|
use crate::types::instance::{NominalInstanceType, Protocol, ProtocolInstanceType};
|
||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
KnownInstanceType, Type, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarVariance,
|
KnownInstanceType, Type, TypeMapping, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||||
UnionType, declaration_type, todo_type,
|
TypeVarVariance, UnionType, declaration_type, todo_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
@ -219,11 +221,12 @@ impl<'db> GenericContext<'db> {
|
||||||
// Typevars are only allowed to refer to _earlier_ typevars in their defaults. (This is
|
// Typevars are only allowed to refer to _earlier_ typevars in their defaults. (This is
|
||||||
// statically enforced for PEP-695 contexts, and is explicitly called out as a
|
// statically enforced for PEP-695 contexts, and is explicitly called out as a
|
||||||
// requirement for legacy contexts.)
|
// requirement for legacy contexts.)
|
||||||
let type_mapping = TypeMapping::Partial {
|
let partial = PartialSpecialization {
|
||||||
generic_context: self,
|
generic_context: self,
|
||||||
types: &expanded[0..idx],
|
types: Cow::Borrowed(&expanded[0..idx]),
|
||||||
};
|
};
|
||||||
let default = default.apply_type_mapping(db, type_mapping);
|
let default =
|
||||||
|
default.apply_type_mapping(db, &TypeMapping::PartialSpecialization(partial));
|
||||||
expanded[idx] = default;
|
expanded[idx] = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,8 +298,14 @@ pub struct Specialization<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Specialization<'db> {
|
impl<'db> Specialization<'db> {
|
||||||
pub(crate) fn type_mapping(self) -> TypeMapping<'db, 'db> {
|
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
|
||||||
TypeMapping::Specialization(self)
|
/// mapping.
|
||||||
|
pub(crate) fn get(self, db: &'db dyn Db, typevar: TypeVarInstance<'db>) -> Option<Type<'db>> {
|
||||||
|
let index = self
|
||||||
|
.generic_context(db)
|
||||||
|
.variables(db)
|
||||||
|
.get_index_of(&typevar)?;
|
||||||
|
self.types(db).get(index).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies a specialization to this specialization. This is used, for instance, when a generic
|
/// Applies a specialization to this specialization. This is used, for instance, when a generic
|
||||||
|
@ -313,13 +322,13 @@ impl<'db> Specialization<'db> {
|
||||||
/// That lets us produce the generic alias `A[int]`, which is the corresponding entry in the
|
/// That lets us produce the generic alias `A[int]`, which is the corresponding entry in the
|
||||||
/// MRO of `B[int]`.
|
/// MRO of `B[int]`.
|
||||||
pub(crate) fn apply_specialization(self, db: &'db dyn Db, other: Specialization<'db>) -> Self {
|
pub(crate) fn apply_specialization(self, db: &'db dyn Db, other: Specialization<'db>) -> Self {
|
||||||
self.apply_type_mapping(db, other.type_mapping())
|
self.apply_type_mapping(db, &TypeMapping::Specialization(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_type_mapping<'a>(
|
pub(crate) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let types: Box<[_]> = self
|
let types: Box<[_]> = self
|
||||||
.types(db)
|
.types(db)
|
||||||
|
@ -527,49 +536,24 @@ impl<'db> Specialization<'db> {
|
||||||
///
|
///
|
||||||
/// You will usually use [`Specialization`] instead of this type. This type is used when we need to
|
/// You will usually use [`Specialization`] instead of this type. This type is used when we need to
|
||||||
/// substitute types for type variables before we have fully constructed a [`Specialization`].
|
/// substitute types for type variables before we have fully constructed a [`Specialization`].
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub(crate) enum TypeMapping<'a, 'db> {
|
pub struct PartialSpecialization<'a, 'db> {
|
||||||
Specialization(Specialization<'db>),
|
|
||||||
Partial {
|
|
||||||
generic_context: GenericContext<'db>,
|
generic_context: GenericContext<'db>,
|
||||||
types: &'a [Type<'db>],
|
types: Cow<'a, [Type<'db>]>,
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'db> TypeMapping<'_, 'db> {
|
|
||||||
fn generic_context(self, db: &'db dyn Db) -> GenericContext<'db> {
|
|
||||||
match self {
|
|
||||||
Self::Specialization(specialization) => specialization.generic_context(db),
|
|
||||||
Self::Partial {
|
|
||||||
generic_context, ..
|
|
||||||
} => generic_context,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'db> PartialSpecialization<'_, 'db> {
|
||||||
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
|
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
|
||||||
/// mapping.
|
/// mapping.
|
||||||
pub(crate) fn get(self, db: &'db dyn Db, typevar: TypeVarInstance<'db>) -> Option<Type<'db>> {
|
pub(crate) fn get(&self, db: &'db dyn Db, typevar: TypeVarInstance<'db>) -> Option<Type<'db>> {
|
||||||
let index = self
|
let index = self.generic_context.variables(db).get_index_of(&typevar)?;
|
||||||
.generic_context(db)
|
self.types.get(index).copied()
|
||||||
.variables(db)
|
|
||||||
.get_index_of(&typevar)?;
|
|
||||||
match self {
|
|
||||||
Self::Specialization(specialization) => specialization.types(db).get(index).copied(),
|
|
||||||
Self::Partial { types, .. } => types.get(index).copied(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
pub(crate) fn to_owned(&self) -> PartialSpecialization<'db, 'db> {
|
||||||
match self {
|
PartialSpecialization {
|
||||||
Self::Specialization(specialization) => specialization,
|
generic_context: self.generic_context,
|
||||||
Self::Partial {
|
types: Cow::from(self.types.clone().into_owned()),
|
||||||
generic_context,
|
|
||||||
types,
|
|
||||||
} => {
|
|
||||||
let mut types = types.to_vec();
|
|
||||||
types.resize(generic_context.variables(db).len(), Type::unknown());
|
|
||||||
Specialization::new(db, generic_context, types.into_boxed_slice())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2010,7 +2010,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
.to_scope_id(self.db(), self.file());
|
.to_scope_id(self.db(), self.file());
|
||||||
|
|
||||||
let inherited_generic_context = None;
|
let inherited_generic_context = None;
|
||||||
let specialization = None;
|
let type_mappings = Box::from([]);
|
||||||
|
|
||||||
let mut inferred_ty = Type::FunctionLiteral(FunctionType::new(
|
let mut inferred_ty = Type::FunctionLiteral(FunctionType::new(
|
||||||
self.db(),
|
self.db(),
|
||||||
|
@ -2020,7 +2020,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
function_decorators,
|
function_decorators,
|
||||||
dataclass_transformer_params,
|
dataclass_transformer_params,
|
||||||
inherited_generic_context,
|
inherited_generic_context,
|
||||||
specialization,
|
type_mappings,
|
||||||
));
|
));
|
||||||
|
|
||||||
for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() {
|
for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() {
|
||||||
|
|
|
@ -5,8 +5,7 @@ use std::marker::PhantomData;
|
||||||
use super::protocol_class::ProtocolInterface;
|
use super::protocol_class::ProtocolInterface;
|
||||||
use super::{ClassType, KnownClass, SubclassOfType, Type};
|
use super::{ClassType, KnownClass, SubclassOfType, Type};
|
||||||
use crate::symbol::{Symbol, SymbolAndQualifiers};
|
use crate::symbol::{Symbol, SymbolAndQualifiers};
|
||||||
use crate::types::generics::TypeMapping;
|
use crate::types::{ClassLiteral, TypeMapping, TypeVarInstance};
|
||||||
use crate::types::{ClassLiteral, TypeVarInstance};
|
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||||
|
@ -139,7 +138,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
pub(super) fn apply_type_mapping<'a>(
|
pub(super) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_class(self.class.apply_type_mapping(db, type_mapping))
|
Self::from_class(self.class.apply_type_mapping(db, type_mapping))
|
||||||
}
|
}
|
||||||
|
@ -312,7 +311,7 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||||
pub(super) fn apply_type_mapping<'a>(
|
pub(super) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Protocol::FromClass(class) => {
|
Protocol::FromClass(class) => {
|
||||||
|
@ -364,9 +363,8 @@ impl<'db> Protocol<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod synthesized_protocol {
|
mod synthesized_protocol {
|
||||||
use crate::types::TypeVarInstance;
|
|
||||||
use crate::types::generics::TypeMapping;
|
|
||||||
use crate::types::protocol_class::ProtocolInterface;
|
use crate::types::protocol_class::ProtocolInterface;
|
||||||
|
use crate::types::{TypeMapping, TypeVarInstance};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
|
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
|
||||||
|
@ -389,7 +387,7 @@ mod synthesized_protocol {
|
||||||
pub(super) fn apply_type_mapping<'a>(
|
pub(super) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self(self.0.specialized_and_normalized(db, type_mapping))
|
Self(self.0.specialized_and_normalized(db, type_mapping))
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
pub(super) fn specialized_and_normalized<'a>(
|
pub(super) fn specialized_and_normalized<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
||||||
|
@ -226,7 +226,7 @@ impl<'db> ProtocolMemberData<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ty: self.ty.apply_type_mapping(db, type_mapping),
|
ty: self.ty.apply_type_mapping(db, type_mapping),
|
||||||
qualifiers: self.qualifiers,
|
qualifiers: self.qualifiers,
|
||||||
|
|
|
@ -17,8 +17,8 @@ use smallvec::{SmallVec, smallvec};
|
||||||
|
|
||||||
use super::{DynamicType, Type, definition_expression_type};
|
use super::{DynamicType, Type, definition_expression_type};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::types::generics::{GenericContext, Specialization, TypeMapping};
|
use crate::types::generics::GenericContext;
|
||||||
use crate::types::{ClassLiteral, TypeVarInstance, todo_type};
|
use crate::types::{ClassLiteral, TypeMapping, TypeVarInstance, todo_type};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
use ruff_python_ast::{self as ast, name::Name};
|
use ruff_python_ast::{self as ast, name::Name};
|
||||||
|
|
||||||
|
@ -313,22 +313,10 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_optional_specialization(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
specialization: Option<Specialization<'db>>,
|
|
||||||
) -> Self {
|
|
||||||
if let Some(specialization) = specialization {
|
|
||||||
self.apply_type_mapping(db, specialization.type_mapping())
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn apply_type_mapping<'a>(
|
pub(crate) fn apply_type_mapping<'a>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
generic_context: self.generic_context,
|
generic_context: self.generic_context,
|
||||||
|
@ -1091,7 +1079,7 @@ impl<'db> Parameters<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
value: self
|
value: self
|
||||||
.value
|
.value
|
||||||
|
@ -1263,7 +1251,7 @@ impl<'db> Parameter<'db> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: self
|
annotated_type: self
|
||||||
.annotated_type
|
.annotated_type
|
||||||
|
@ -1468,7 +1456,7 @@ pub(crate) enum ParameterKind<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> ParameterKind<'db> {
|
impl<'db> ParameterKind<'db> {
|
||||||
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::PositionalOnly { default_type, name } => Self::PositionalOnly {
|
Self::PositionalOnly { default_type, name } => Self::PositionalOnly {
|
||||||
default_type: default_type
|
default_type: default_type
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::symbol::SymbolAndQualifiers;
|
use crate::symbol::SymbolAndQualifiers;
|
||||||
use crate::types::generics::TypeMapping;
|
use crate::types::{
|
||||||
|
ClassType, DynamicType, KnownClass, MemberLookupPolicy, Type, TypeMapping, TypeVarInstance,
|
||||||
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
use super::{ClassType, DynamicType, KnownClass, MemberLookupPolicy, Type, TypeVarInstance};
|
|
||||||
|
|
||||||
/// A type that represents `type[C]`, i.e. the class object `C` and class objects that are subclasses of `C`.
|
/// A type that represents `type[C]`, i.e. the class object `C` and class objects that are subclasses of `C`.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||||
pub struct SubclassOfType<'db> {
|
pub struct SubclassOfType<'db> {
|
||||||
|
@ -71,7 +71,7 @@ impl<'db> SubclassOfType<'db> {
|
||||||
pub(super) fn apply_type_mapping<'a>(
|
pub(super) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self.subclass_of {
|
match self.subclass_of {
|
||||||
SubclassOfInner::Class(class) => Self {
|
SubclassOfInner::Class(class) => Self {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue