[ty] Type-context aware literal promotion (#20776)

## Summary

Avoid literal promotion when a literal type annotation is provided, e.g.,
```py
x: list[Literal[1]] = [1]
```

Resolves https://github.com/astral-sh/ty/issues/1198. This does not fix
issue https://github.com/astral-sh/ty/issues/1284, but it does make it
more relevant because after this change, it is possible to directly
instantiate a generic type with a literal specialization.
This commit is contained in:
Ibraheem Ahmed 2025-10-09 16:53:53 -04:00 committed by GitHub
parent 537ec5f012
commit b086ffe921
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 445 additions and 151 deletions

View File

@ -130,13 +130,9 @@ type IntList = list[int]
m: IntList = [1, 2, 3] m: IntList = [1, 2, 3]
reveal_type(m) # revealed: list[int] reveal_type(m) # revealed: list[int]
# TODO: this should type-check and avoid literal promotion
# error: [invalid-assignment] "Object of type `list[Unknown | int]` is not assignable to `list[Literal[1, 2, 3]]`"
n: list[typing.Literal[1, 2, 3]] = [1, 2, 3] n: list[typing.Literal[1, 2, 3]] = [1, 2, 3]
reveal_type(n) # revealed: list[Literal[1, 2, 3]] reveal_type(n) # revealed: list[Literal[1, 2, 3]]
# TODO: this should type-check and avoid literal promotion
# error: [invalid-assignment] "Object of type `list[Unknown | str]` is not assignable to `list[LiteralString]`"
o: list[typing.LiteralString] = ["a", "b", "c"] o: list[typing.LiteralString] = ["a", "b", "c"]
reveal_type(o) # revealed: list[LiteralString] reveal_type(o) # revealed: list[LiteralString]
@ -160,6 +156,81 @@ a: list[str] = [1, 2, 3]
b: set[int] = {1, 2, "3"} b: set[int] = {1, 2, "3"}
``` ```
## Literal annnotations are respected
```toml
[environment]
python-version = "3.12"
```
```py
from enum import Enum
from typing_extensions import Literal, LiteralString
a: list[Literal[1]] = [1]
reveal_type(a) # revealed: list[Literal[1]]
b: list[Literal[True]] = [True]
reveal_type(b) # revealed: list[Literal[True]]
c: list[Literal["a"]] = ["a"]
reveal_type(c) # revealed: list[Literal["a"]]
d: list[LiteralString] = ["a", "b", "c"]
reveal_type(d) # revealed: list[LiteralString]
e: list[list[Literal[1]]] = [[1]]
reveal_type(e) # revealed: list[list[Literal[1]]]
class Color(Enum):
RED = "red"
f: dict[list[Literal[1]], list[Literal[Color.RED]]] = {[1]: [Color.RED, Color.RED]}
reveal_type(f) # revealed: dict[list[Literal[1]], list[Literal[Color.RED]]]
class X[T]:
def __init__(self, value: T): ...
g: X[Literal[1]] = X(1)
reveal_type(g) # revealed: X[Literal[1]]
h: X[int] = X(1)
reveal_type(h) # revealed: X[int]
i: dict[list[X[Literal[1]]], set[Literal[b"a"]]] = {[X(1)]: {b"a"}}
reveal_type(i) # revealed: dict[list[X[Literal[1]]], set[Literal[b"a"]]]
j: list[Literal[1, 2, 3]] = [1, 2, 3]
reveal_type(j) # revealed: list[Literal[1, 2, 3]]
k: list[Literal[1] | Literal[2] | Literal[3]] = [1, 2, 3]
reveal_type(k) # revealed: list[Literal[1, 2, 3]]
type Y[T] = list[T]
l: Y[Y[Literal[1]]] = [[1]]
reveal_type(l) # revealed: list[list[Literal[1]]]
m: list[tuple[Literal[1], Literal[2], Literal[3]]] = [(1, 2, 3)]
reveal_type(m) # revealed: list[tuple[Literal[1], Literal[2], Literal[3]]]
n: list[tuple[int, str, int]] = [(1, "2", 3), (4, "5", 6)]
reveal_type(n) # revealed: list[tuple[int, str, int]]
o: list[tuple[Literal[1], ...]] = [(1, 1, 1)]
reveal_type(o) # revealed: list[tuple[Literal[1], ...]]
p: list[tuple[int, ...]] = [(1, 1, 1)]
reveal_type(p) # revealed: list[tuple[int, ...]]
# literal promotion occurs based on assignability, an exact match is not required
q: list[int | Literal[1]] = [1]
reveal_type(q) # revealed: list[int]
r: list[Literal[1, 2, 3, 4]] = [1, 2]
reveal_type(r) # revealed: list[Literal[1, 2, 3, 4]]
```
## PEP-604 annotations are supported ## PEP-604 annotations are supported
```py ```py

View File

@ -525,10 +525,6 @@ from typing import Literal
reveal_type(list((1, 2, 3))) # revealed: list[int] reveal_type(list((1, 2, 3))) # revealed: list[int]
reveal_type(list(((1, 2, 3),))) # revealed: list[tuple[int, int, int]] reveal_type(list(((1, 2, 3),))) # revealed: list[tuple[int, int, int]]
# TODO: we could bidirectionally infer that the user does not want literals to be promoted here,
# and avoid this diagnostic
#
# error: [invalid-assignment] "`list[int]` is not assignable to `list[Literal[1, 2, 3]]`"
x: list[Literal[1, 2, 3]] = list((1, 2, 3)) x: list[Literal[1, 2, 3]] = list((1, 2, 3))
reveal_type(x) # revealed: list[Literal[1, 2, 3]] reveal_type(x) # revealed: list[Literal[1, 2, 3]]
``` ```

View File

@ -524,14 +524,15 @@ impl<'db> PropertyInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
let getter = self let getter = self
.getter(db) .getter(db)
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)); .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor));
let setter = self let setter = self
.setter(db) .setter(db)
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)); .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor));
Self::new(db, getter, setter) Self::new(db, getter, setter)
} }
@ -825,6 +826,7 @@ impl<'db> Type<'db> {
db, db,
dunder_name, dunder_name,
&mut CallArguments::positional([Type::unknown()]), &mut CallArguments::positional([Type::unknown()]),
TypeContext::default(),
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK
| MemberLookupPolicy::MRO_NO_INT_OR_STR_LOOKUP, | MemberLookupPolicy::MRO_NO_INT_OR_STR_LOOKUP,
); );
@ -856,9 +858,21 @@ impl<'db> Type<'db> {
// If the type is a specialized instance of the given `KnownClass`, returns the specialization. // If the type is a specialized instance of the given `KnownClass`, returns the specialization.
pub(crate) fn known_specialization( pub(crate) fn known_specialization(
self, &self,
known_class: KnownClass,
db: &'db dyn Db, db: &'db dyn Db,
known_class: KnownClass,
) -> Option<Specialization<'db>> {
let class_literal = known_class.try_to_class_literal(db)?;
self.specialization_of(db, Some(class_literal))
}
// If the type is a specialized instance of the given class, returns the specialization.
//
// If no class is provided, returns the specialization of any class instance.
pub(crate) fn specialization_of(
self,
db: &'db dyn Db,
expected_class: Option<ClassLiteral<'_>>,
) -> Option<Specialization<'db>> { ) -> Option<Specialization<'db>> {
let class_type = match self { let class_type = match self {
Type::NominalInstance(instance) => instance, Type::NominalInstance(instance) => instance,
@ -867,13 +881,12 @@ impl<'db> Type<'db> {
} }
.class(db); .class(db);
if !class_type.is_known(db, known_class) { let (class_literal, specialization) = class_type.class_literal(db);
if expected_class.is_some_and(|expected_class| expected_class != class_literal) {
return None; return None;
} }
class_type specialization
.into_generic_alias()
.map(|generic_alias| generic_alias.specialization(db))
} }
/// Returns the top materialization (or upper bound materialization) of this type, which is the /// Returns the top materialization (or upper bound materialization) of this type, which is the
@ -945,7 +958,12 @@ impl<'db> Type<'db> {
materialization_kind: MaterializationKind, materialization_kind: MaterializationKind,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Type<'db> { ) -> Type<'db> {
self.apply_type_mapping_impl(db, &TypeMapping::Materialize(materialization_kind), visitor) self.apply_type_mapping_impl(
db,
&TypeMapping::Materialize(materialization_kind),
TypeContext::default(),
visitor,
)
} }
pub(crate) const fn is_type_var(self) -> bool { pub(crate) const fn is_type_var(self) -> bool {
@ -1180,22 +1198,35 @@ impl<'db> Type<'db> {
/// Note that this function tries to promote literals to a more user-friendly form than their /// Note that this function tries to promote literals to a more user-friendly form than their
/// fallback instance type. For example, `def _() -> int` is promoted to `Callable[[], int]`, /// fallback instance type. For example, `def _() -> int` is promoted to `Callable[[], int]`,
/// as opposed to `FunctionType`. /// as opposed to `FunctionType`.
pub(crate) fn promote_literals(self, db: &'db dyn Db) -> Type<'db> { ///
self.apply_type_mapping(db, &TypeMapping::PromoteLiterals) /// It also avoids literal promotion if a literal type annotation was provided as type context.
pub(crate) fn promote_literals(self, db: &'db dyn Db, tcx: TypeContext<'db>) -> Type<'db> {
self.apply_type_mapping(db, &TypeMapping::PromoteLiterals, tcx)
} }
/// Like [`Type::promote_literals`], but does not recurse into nested types. /// Like [`Type::promote_literals`], but does not recurse into nested types.
fn promote_literals_impl(self, db: &'db dyn Db) -> Type<'db> { fn promote_literals_impl(self, db: &'db dyn Db, tcx: TypeContext<'db>) -> Type<'db> {
match self { let promoted = match self {
Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_instance(db), Type::StringLiteral(_) => KnownClass::Str.to_instance(db),
Type::LiteralString => KnownClass::Str.to_instance(db),
Type::BooleanLiteral(_) => KnownClass::Bool.to_instance(db), Type::BooleanLiteral(_) => KnownClass::Bool.to_instance(db),
Type::IntLiteral(_) => KnownClass::Int.to_instance(db), Type::IntLiteral(_) => KnownClass::Int.to_instance(db),
Type::BytesLiteral(_) => KnownClass::Bytes.to_instance(db), Type::BytesLiteral(_) => KnownClass::Bytes.to_instance(db),
Type::ModuleLiteral(_) => KnownClass::ModuleType.to_instance(db), Type::ModuleLiteral(_) => KnownClass::ModuleType.to_instance(db),
Type::EnumLiteral(literal) => literal.enum_class_instance(db), Type::EnumLiteral(literal) => literal.enum_class_instance(db),
Type::FunctionLiteral(literal) => Type::Callable(literal.into_callable_type(db)), Type::FunctionLiteral(literal) => Type::Callable(literal.into_callable_type(db)),
_ => self, _ => return self,
};
// Avoid literal promotion if it leads to an unassignable type.
if tcx
.annotation
.is_none_or(|annotation| promoted.is_assignable_to(db, annotation))
{
return promoted;
} }
self
} }
/// 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.
@ -3973,6 +4004,7 @@ impl<'db> Type<'db> {
db, db,
"__getattr__", "__getattr__",
CallArguments::positional([Type::string_literal(db, &name)]), CallArguments::positional([Type::string_literal(db, &name)]),
TypeContext::default(),
) )
.map(|outcome| Place::bound(outcome.return_type(db))) .map(|outcome| Place::bound(outcome.return_type(db)))
// TODO: Handle call errors here. // TODO: Handle call errors here.
@ -3992,6 +4024,7 @@ impl<'db> Type<'db> {
db, db,
"__getattribute__", "__getattribute__",
&mut CallArguments::positional([Type::string_literal(db, &name)]), &mut CallArguments::positional([Type::string_literal(db, &name)]),
TypeContext::default(),
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK, MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
) )
.map(|outcome| Place::bound(outcome.return_type(db))) .map(|outcome| Place::bound(outcome.return_type(db)))
@ -4139,7 +4172,12 @@ impl<'db> Type<'db> {
// runtime there is a fallback to `__len__`, since `__bool__` takes precedence // runtime there is a fallback to `__len__`, since `__bool__` takes precedence
// and a subclass could add a `__bool__` method. // and a subclass could add a `__bool__` method.
match self.try_call_dunder(db, "__bool__", CallArguments::none()) { match self.try_call_dunder(
db,
"__bool__",
CallArguments::none(),
TypeContext::default(),
) {
Ok(outcome) => { Ok(outcome) => {
let return_type = outcome.return_type(db); let return_type = outcome.return_type(db);
if !return_type.is_assignable_to(db, KnownClass::Bool.to_instance(db)) { if !return_type.is_assignable_to(db, KnownClass::Bool.to_instance(db)) {
@ -4354,7 +4392,12 @@ impl<'db> Type<'db> {
return usize_len.try_into().ok().map(Type::IntLiteral); return usize_len.try_into().ok().map(Type::IntLiteral);
} }
let return_ty = match self.try_call_dunder(db, "__len__", CallArguments::none()) { let return_ty = match self.try_call_dunder(
db,
"__len__",
CallArguments::none(),
TypeContext::default(),
) {
Ok(bindings) => bindings.return_type(db), Ok(bindings) => bindings.return_type(db),
Err(CallDunderError::PossiblyUnbound(bindings)) => bindings.return_type(db), Err(CallDunderError::PossiblyUnbound(bindings)) => bindings.return_type(db),
@ -5181,11 +5224,13 @@ impl<'db> Type<'db> {
db: &'db dyn Db, db: &'db dyn Db,
name: &str, name: &str,
mut argument_types: CallArguments<'_, 'db>, mut argument_types: CallArguments<'_, 'db>,
tcx: TypeContext<'db>,
) -> Result<Bindings<'db>, CallDunderError<'db>> { ) -> Result<Bindings<'db>, CallDunderError<'db>> {
self.try_call_dunder_with_policy( self.try_call_dunder_with_policy(
db, db,
name, name,
&mut argument_types, &mut argument_types,
tcx,
MemberLookupPolicy::default(), MemberLookupPolicy::default(),
) )
} }
@ -5202,6 +5247,7 @@ impl<'db> Type<'db> {
db: &'db dyn Db, db: &'db dyn Db,
name: &str, name: &str,
argument_types: &mut CallArguments<'_, 'db>, argument_types: &mut CallArguments<'_, 'db>,
tcx: TypeContext<'db>,
policy: MemberLookupPolicy, policy: MemberLookupPolicy,
) -> Result<Bindings<'db>, CallDunderError<'db>> { ) -> Result<Bindings<'db>, CallDunderError<'db>> {
// Implicit calls to dunder methods never access instance members, so we pass // Implicit calls to dunder methods never access instance members, so we pass
@ -5218,7 +5264,7 @@ impl<'db> Type<'db> {
let bindings = dunder_callable let bindings = dunder_callable
.bindings(db) .bindings(db)
.match_parameters(db, argument_types) .match_parameters(db, argument_types)
.check_types(db, argument_types, &TypeContext::default())?; .check_types(db, argument_types, &tcx)?;
if boundness == Boundness::PossiblyUnbound { if boundness == Boundness::PossiblyUnbound {
return Err(CallDunderError::PossiblyUnbound(Box::new(bindings))); return Err(CallDunderError::PossiblyUnbound(Box::new(bindings)));
} }
@ -5266,11 +5312,21 @@ impl<'db> Type<'db> {
CallDunderError<'db>, CallDunderError<'db>,
> { > {
iterator iterator
.try_call_dunder(db, "__anext__", CallArguments::none()) .try_call_dunder(
db,
"__anext__",
CallArguments::none(),
TypeContext::default(),
)
.map(|dunder_anext_outcome| dunder_anext_outcome.return_type(db).try_await(db)) .map(|dunder_anext_outcome| dunder_anext_outcome.return_type(db).try_await(db))
}; };
return match self.try_call_dunder(db, "__aiter__", CallArguments::none()) { return match self.try_call_dunder(
db,
"__aiter__",
CallArguments::none(),
TypeContext::default(),
) {
Ok(dunder_aiter_bindings) => { Ok(dunder_aiter_bindings) => {
let iterator = dunder_aiter_bindings.return_type(db); let iterator = dunder_aiter_bindings.return_type(db);
match try_call_dunder_anext_on_iterator(iterator) { match try_call_dunder_anext_on_iterator(iterator) {
@ -5414,18 +5470,29 @@ impl<'db> Type<'db> {
db, db,
"__getitem__", "__getitem__",
CallArguments::positional([KnownClass::Int.to_instance(db)]), CallArguments::positional([KnownClass::Int.to_instance(db)]),
TypeContext::default(),
) )
.map(|dunder_getitem_outcome| dunder_getitem_outcome.return_type(db)) .map(|dunder_getitem_outcome| dunder_getitem_outcome.return_type(db))
}; };
let try_call_dunder_next_on_iterator = |iterator: Type<'db>| { let try_call_dunder_next_on_iterator = |iterator: Type<'db>| {
iterator iterator
.try_call_dunder(db, "__next__", CallArguments::none()) .try_call_dunder(
db,
"__next__",
CallArguments::none(),
TypeContext::default(),
)
.map(|dunder_next_outcome| dunder_next_outcome.return_type(db)) .map(|dunder_next_outcome| dunder_next_outcome.return_type(db))
}; };
let dunder_iter_result = self let dunder_iter_result = self
.try_call_dunder(db, "__iter__", CallArguments::none()) .try_call_dunder(
db,
"__iter__",
CallArguments::none(),
TypeContext::default(),
)
.map(|dunder_iter_outcome| dunder_iter_outcome.return_type(db)); .map(|dunder_iter_outcome| dunder_iter_outcome.return_type(db));
match dunder_iter_result { match dunder_iter_result {
@ -5533,11 +5600,17 @@ impl<'db> Type<'db> {
EvaluationMode::Sync => ("__enter__", "__exit__"), EvaluationMode::Sync => ("__enter__", "__exit__"),
}; };
let enter = self.try_call_dunder(db, enter_method, CallArguments::none()); let enter = self.try_call_dunder(
db,
enter_method,
CallArguments::none(),
TypeContext::default(),
);
let exit = self.try_call_dunder( let exit = self.try_call_dunder(
db, db,
exit_method, exit_method,
CallArguments::positional([Type::none(db), Type::none(db), Type::none(db)]), CallArguments::positional([Type::none(db), Type::none(db), Type::none(db)]),
TypeContext::default(),
); );
// TODO: Make use of Protocols when we support it (the manager be assignable to `contextlib.AbstractContextManager`). // TODO: Make use of Protocols when we support it (the manager be assignable to `contextlib.AbstractContextManager`).
@ -5574,7 +5647,12 @@ impl<'db> Type<'db> {
/// Resolve the type of an `await …` expression where `self` is the type of the awaitable. /// Resolve the type of an `await …` expression where `self` is the type of the awaitable.
fn try_await(self, db: &'db dyn Db) -> Result<Type<'db>, AwaitError<'db>> { fn try_await(self, db: &'db dyn Db) -> Result<Type<'db>, AwaitError<'db>> {
let await_result = self.try_call_dunder(db, "__await__", CallArguments::none()); let await_result = self.try_call_dunder(
db,
"__await__",
CallArguments::none(),
TypeContext::default(),
);
match await_result { match await_result {
Ok(bindings) => { Ok(bindings) => {
let return_type = bindings.return_type(db); let return_type = bindings.return_type(db);
@ -5642,6 +5720,7 @@ impl<'db> Type<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
argument_types: CallArguments<'_, 'db>, argument_types: CallArguments<'_, 'db>,
tcx: TypeContext<'db>,
) -> Result<Type<'db>, ConstructorCallError<'db>> { ) -> Result<Type<'db>, ConstructorCallError<'db>> {
debug_assert!(matches!( debug_assert!(matches!(
self, self,
@ -5729,7 +5808,7 @@ impl<'db> Type<'db> {
.place .place
.is_unbound() .is_unbound()
{ {
Some(init_ty.try_call_dunder(db, "__init__", argument_types)) Some(init_ty.try_call_dunder(db, "__init__", argument_types, tcx))
} else { } else {
None None
}; };
@ -6274,8 +6353,11 @@ impl<'db> Type<'db> {
db: &'db dyn Db, db: &'db dyn Db,
specialization: Specialization<'db>, specialization: Specialization<'db>,
) -> Type<'db> { ) -> Type<'db> {
let new_specialization = let new_specialization = self.apply_type_mapping(
self.apply_type_mapping(db, &TypeMapping::Specialization(specialization)); db,
&TypeMapping::Specialization(specialization),
TypeContext::default(),
);
match specialization.materialization_kind(db) { match specialization.materialization_kind(db) {
None => new_specialization, None => new_specialization,
Some(materialization_kind) => new_specialization.materialize( Some(materialization_kind) => new_specialization.materialize(
@ -6290,14 +6372,16 @@ impl<'db> Type<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
) -> Type<'db> { ) -> Type<'db> {
self.apply_type_mapping_impl(db, type_mapping, &ApplyTypeMappingVisitor::default()) self.apply_type_mapping_impl(db, type_mapping, tcx, &ApplyTypeMappingVisitor::default())
} }
fn apply_type_mapping_impl<'a>( fn apply_type_mapping_impl<'a>(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Type<'db> { ) -> Type<'db> {
match self { match self {
@ -6373,22 +6457,23 @@ impl<'db> Type<'db> {
} }
Type::FunctionLiteral(function) => { Type::FunctionLiteral(function) => {
let function = Type::FunctionLiteral(function.apply_type_mapping_impl(db, type_mapping, visitor)); let function = Type::FunctionLiteral(function.apply_type_mapping_impl(db, type_mapping, tcx, visitor));
match type_mapping { match type_mapping {
TypeMapping::PromoteLiterals => function.promote_literals_impl(db), TypeMapping::PromoteLiterals => function.promote_literals_impl(db, tcx),
_ => function _ => function
} }
} }
Type::BoundMethod(method) => Type::BoundMethod(BoundMethodType::new( Type::BoundMethod(method) => Type::BoundMethod(BoundMethodType::new(
db, db,
method.function(db).apply_type_mapping_impl(db, type_mapping, visitor), method.function(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
method.self_instance(db).apply_type_mapping_impl(db, type_mapping, visitor), method.self_instance(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)), )),
Type::NominalInstance(instance) => Type::NominalInstance(instance) => {
instance.apply_type_mapping_impl(db, type_mapping, visitor), instance.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
},
Type::ProtocolInstance(instance) => { Type::ProtocolInstance(instance) => {
// TODO: Add tests for materialization once subtyping/assignability is implemented for // TODO: Add tests for materialization once subtyping/assignability is implemented for
@ -6398,59 +6483,59 @@ impl<'db> Type<'db> {
// > read-only property members, and method members, on protocols act covariantly; // > read-only property members, and method members, on protocols act covariantly;
// > write-only property members act contravariantly; and read/write attribute // > write-only property members act contravariantly; and read/write attribute
// > members on protocols act invariantly // > members on protocols act invariantly
Type::ProtocolInstance(instance.apply_type_mapping_impl(db, type_mapping, visitor)) Type::ProtocolInstance(instance.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderGet(function)) => { Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderGet(function)) => {
Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderGet( Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderGet(
function.apply_type_mapping_impl(db, type_mapping, visitor), function.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)) ))
} }
Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderCall(function)) => { Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderCall(function)) => {
Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderCall( Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderCall(
function.apply_type_mapping_impl(db, type_mapping, visitor), function.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)) ))
} }
Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderGet(property)) => { Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderGet(property)) => {
Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderGet( Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderGet(
property.apply_type_mapping_impl(db, type_mapping, visitor), property.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)) ))
} }
Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderSet(property)) => { Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderSet(property)) => {
Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderSet( Type::KnownBoundMethod(KnownBoundMethodType::PropertyDunderSet(
property.apply_type_mapping_impl(db, type_mapping, visitor), property.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)) ))
} }
Type::Callable(callable) => { Type::Callable(callable) => {
Type::Callable(callable.apply_type_mapping_impl(db, type_mapping, visitor)) Type::Callable(callable.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Type::GenericAlias(generic) => { Type::GenericAlias(generic) => {
Type::GenericAlias(generic.apply_type_mapping_impl(db, type_mapping, visitor)) Type::GenericAlias(generic.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Type::TypedDict(typed_dict) => { Type::TypedDict(typed_dict) => {
Type::TypedDict(typed_dict.apply_type_mapping_impl(db, type_mapping, visitor)) Type::TypedDict(typed_dict.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Type::SubclassOf(subclass_of) => subclass_of.apply_type_mapping_impl(db, type_mapping, visitor), Type::SubclassOf(subclass_of) => subclass_of.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
Type::PropertyInstance(property) => { Type::PropertyInstance(property) => {
Type::PropertyInstance(property.apply_type_mapping_impl(db, type_mapping, visitor)) Type::PropertyInstance(property.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Type::Union(union) => union.map(db, |element| { Type::Union(union) => union.map(db, |element| {
element.apply_type_mapping_impl(db, type_mapping, visitor) element.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
}), }),
Type::Intersection(intersection) => { Type::Intersection(intersection) => {
let mut builder = IntersectionBuilder::new(db); let mut builder = IntersectionBuilder::new(db);
for positive in intersection.positive(db) { for positive in intersection.positive(db) {
builder = builder =
builder.add_positive(positive.apply_type_mapping_impl(db, type_mapping, visitor)); builder.add_positive(positive.apply_type_mapping_impl(db, type_mapping, tcx, visitor));
} }
let flipped_mapping = match type_mapping { let flipped_mapping = match type_mapping {
TypeMapping::Materialize(materialization_kind) => &TypeMapping::Materialize(materialization_kind.flip()), TypeMapping::Materialize(materialization_kind) => &TypeMapping::Materialize(materialization_kind.flip()),
@ -6458,16 +6543,16 @@ impl<'db> Type<'db> {
}; };
for negative in intersection.negative(db) { for negative in intersection.negative(db) {
builder = builder =
builder.add_negative(negative.apply_type_mapping_impl(db, flipped_mapping, visitor)); builder.add_negative(negative.apply_type_mapping_impl(db, flipped_mapping, tcx, visitor));
} }
builder.build() builder.build()
} }
// TODO(jelle): Materialize should be handled differently, since TypeIs is invariant // TODO(jelle): Materialize should be handled differently, since TypeIs is invariant
Type::TypeIs(type_is) => type_is.with_type(db, type_is.return_type(db).apply_type_mapping(db, type_mapping)), Type::TypeIs(type_is) => type_is.with_type(db, type_is.return_type(db).apply_type_mapping(db, type_mapping, tcx)),
Type::TypeAlias(alias) => { Type::TypeAlias(alias) => {
visitor.visit(self, || alias.value_type(db).apply_type_mapping_impl(db, type_mapping, visitor)) visitor.visit(self, || alias.value_type(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Type::ModuleLiteral(_) Type::ModuleLiteral(_)
@ -6484,7 +6569,7 @@ impl<'db> Type<'db> {
TypeMapping::ReplaceSelf { .. } | TypeMapping::ReplaceSelf { .. } |
TypeMapping::MarkTypeVarsInferable(_) | TypeMapping::MarkTypeVarsInferable(_) |
TypeMapping::Materialize(_) => self, TypeMapping::Materialize(_) => self,
TypeMapping::PromoteLiterals => self.promote_literals_impl(db) TypeMapping::PromoteLiterals => self.promote_literals_impl(db, tcx)
} }
Type::Dynamic(_) => match type_mapping { Type::Dynamic(_) => match type_mapping {
@ -7931,12 +8016,14 @@ impl<'db> TypeVarInstance<'db> {
}), }),
self.explicit_variance(db), self.explicit_variance(db),
self._default(db).and_then(|default| match default { self._default(db).and_then(|default| match default {
TypeVarDefaultEvaluation::Eager(ty) => { TypeVarDefaultEvaluation::Eager(ty) => Some(
Some(ty.apply_type_mapping_impl(db, type_mapping, visitor).into()) ty.apply_type_mapping_impl(db, type_mapping, TypeContext::default(), visitor)
} .into(),
TypeVarDefaultEvaluation::Lazy => self ),
.lazy_default(db) TypeVarDefaultEvaluation::Lazy => self.lazy_default(db).map(|ty| {
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor).into()), ty.apply_type_mapping_impl(db, type_mapping, TypeContext::default(), visitor)
.into()
}),
}), }),
self.kind(db), self.kind(db),
) )
@ -8151,9 +8238,13 @@ impl<'db> BoundTypeVarInstance<'db> {
/// (resulting in `T@C`). /// (resulting in `T@C`).
pub(crate) fn default_type(self, db: &'db dyn Db) -> Option<Type<'db>> { pub(crate) fn default_type(self, db: &'db dyn Db) -> Option<Type<'db>> {
let binding_context = self.binding_context(db); let binding_context = self.binding_context(db);
self.typevar(db) self.typevar(db).default_type(db).map(|ty| {
.default_type(db) ty.apply_type_mapping(
.map(|ty| ty.apply_type_mapping(db, &TypeMapping::BindLegacyTypevars(binding_context))) db,
&TypeMapping::BindLegacyTypevars(binding_context),
TypeContext::default(),
)
})
} }
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self { pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
@ -8305,7 +8396,7 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
match self { match self {
TypeVarBoundOrConstraints::UpperBound(bound) => TypeVarBoundOrConstraints::UpperBound( TypeVarBoundOrConstraints::UpperBound(bound) => TypeVarBoundOrConstraints::UpperBound(
bound.apply_type_mapping_impl(db, type_mapping, visitor), bound.apply_type_mapping_impl(db, type_mapping, TypeContext::default(), visitor),
), ),
TypeVarBoundOrConstraints::Constraints(constraints) => { TypeVarBoundOrConstraints::Constraints(constraints) => {
TypeVarBoundOrConstraints::Constraints(UnionType::new( TypeVarBoundOrConstraints::Constraints(UnionType::new(
@ -8313,7 +8404,14 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
constraints constraints
.elements(db) .elements(db)
.iter() .iter()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)) .map(|ty| {
ty.apply_type_mapping_impl(
db,
type_mapping,
TypeContext::default(),
visitor,
)
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_boxed_slice(), .into_boxed_slice(),
)) ))
@ -8553,12 +8651,17 @@ impl<'db> ContextManagerError<'db> {
EvaluationMode::Async => ("sync", "__enter__", "__exit__", "with"), EvaluationMode::Async => ("sync", "__enter__", "__exit__", "with"),
}; };
let alt_enter = let alt_enter = context_expression_type.try_call_dunder(
context_expression_type.try_call_dunder(db, alt_enter_method, CallArguments::none()); db,
alt_enter_method,
CallArguments::none(),
TypeContext::default(),
);
let alt_exit = context_expression_type.try_call_dunder( let alt_exit = context_expression_type.try_call_dunder(
db, db,
alt_exit_method, alt_exit_method,
CallArguments::positional([Type::unknown(), Type::unknown(), Type::unknown()]), CallArguments::positional([Type::unknown(), Type::unknown(), Type::unknown()]),
TypeContext::default(),
); );
if (alt_enter.is_ok() || matches!(alt_enter, Err(CallDunderError::CallError(..)))) if (alt_enter.is_ok() || matches!(alt_enter, Err(CallDunderError::CallError(..))))
@ -8654,6 +8757,7 @@ impl<'db> IterationError<'db> {
db, db,
"__anext__", "__anext__",
CallArguments::none(), CallArguments::none(),
TypeContext::default(),
)) ))
.and_then(|ty| ty.try_await(db).ok()) .and_then(|ty| ty.try_await(db).ok())
} else { } else {
@ -8661,6 +8765,7 @@ impl<'db> IterationError<'db> {
db, db,
"__next__", "__next__",
CallArguments::none(), CallArguments::none(),
TypeContext::default(),
)) ))
} }
} }
@ -9704,12 +9809,13 @@ impl<'db> CallableType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
CallableType::new( CallableType::new(
db, db,
self.signatures(db) self.signatures(db)
.apply_type_mapping_impl(db, type_mapping, visitor), .apply_type_mapping_impl(db, type_mapping, tcx, visitor),
self.is_function_like(db), self.is_function_like(db),
) )
} }

View File

@ -2563,7 +2563,10 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
} }
} }
self.specialization = self.signature.generic_context.map(|gc| builder.build(gc)); self.specialization = self
.signature
.generic_context
.map(|gc| builder.build(gc, *self.call_expression_tcx));
} }
fn check_argument_type( fn check_argument_type(

View File

@ -280,13 +280,20 @@ impl<'db> GenericAlias<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
let tcx = tcx
.annotation
.and_then(|ty| ty.specialization_of(db, Some(self.origin(db))))
.map(|specialization| specialization.types(db))
.unwrap_or(&[]);
Self::new( Self::new(
db, db,
self.origin(db), self.origin(db),
self.specialization(db) self.specialization(db)
.apply_type_mapping_impl(db, type_mapping, visitor), .apply_type_mapping_impl(db, type_mapping, tcx, visitor),
) )
} }
@ -469,12 +476,13 @@ impl<'db> ClassType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
match self { match self {
Self::NonGeneric(_) => self, Self::NonGeneric(_) => self,
Self::Generic(generic) => { Self::Generic(generic) => {
Self::Generic(generic.apply_type_mapping_impl(db, type_mapping, visitor)) Self::Generic(generic.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
} }
} }
@ -2030,6 +2038,7 @@ impl<'db> ClassLiteral<'db> {
ClassBase::is_typed_dict, ClassBase::is_typed_dict,
), ),
}, },
TypeContext::default(),
) )
}); });
} }
@ -2337,6 +2346,7 @@ impl<'db> ClassLiteral<'db> {
}, },
), ),
}, },
TypeContext::default(),
) )
}) })
} }
@ -2671,7 +2681,8 @@ impl<'db> ClassLiteral<'db> {
specialization, specialization,
ClassBase::is_typed_dict ClassBase::is_typed_dict
) )
} },
TypeContext::default(),
) )
) )
} }
@ -2921,6 +2932,7 @@ impl<'db> ClassLiteral<'db> {
self.unknown_specialization(db), self.unknown_specialization(db),
), ),
}, },
TypeContext::default(),
) )
}); });
} }

View File

@ -5,7 +5,7 @@ use crate::types::tuple::TupleType;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, ClassLiteral, ClassType, DynamicType, KnownClass, KnownInstanceType, ApplyTypeMappingVisitor, ClassLiteral, ClassType, DynamicType, KnownClass, KnownInstanceType,
MaterializationKind, MroError, MroIterator, NormalizedVisitor, SpecialFormType, Type, MaterializationKind, MroError, MroIterator, NormalizedVisitor, SpecialFormType, Type,
TypeMapping, todo_type, TypeContext, 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.
@ -277,11 +277,12 @@ impl<'db> ClassBase<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
match self { match self {
Self::Class(class) => { Self::Class(class) => {
Self::Class(class.apply_type_mapping_impl(db, type_mapping, visitor)) Self::Class(class.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Self::Dynamic(_) | Self::Generic | Self::Protocol | Self::TypedDict => self, Self::Dynamic(_) | Self::Generic | Self::Protocol | Self::TypedDict => self,
} }
@ -296,6 +297,7 @@ impl<'db> ClassBase<'db> {
let new_self = self.apply_type_mapping_impl( let new_self = self.apply_type_mapping_impl(
db, db,
&TypeMapping::Specialization(specialization), &TypeMapping::Specialization(specialization),
TypeContext::default(),
&ApplyTypeMappingVisitor::default(), &ApplyTypeMappingVisitor::default(),
); );
match specialization.materialization_kind(db) { match specialization.materialization_kind(db) {
@ -311,6 +313,7 @@ impl<'db> ClassBase<'db> {
self.apply_type_mapping_impl( self.apply_type_mapping_impl(
db, db,
&TypeMapping::Materialize(kind), &TypeMapping::Materialize(kind),
TypeContext::default(),
&ApplyTypeMappingVisitor::default(), &ApplyTypeMappingVisitor::default(),
) )
} }

View File

@ -19,7 +19,7 @@ use crate::types::string_annotation::{
}; };
use crate::types::{ use crate::types::{
ClassType, DynamicType, LintDiagnosticGuard, Protocol, ProtocolInstanceType, SubclassOfInner, ClassType, DynamicType, LintDiagnosticGuard, Protocol, ProtocolInstanceType, SubclassOfInner,
binding_type, infer_isolated_expression, TypeContext, binding_type, infer_isolated_expression,
}; };
use crate::types::{SpecialFormType, Type, protocol_class::ProtocolClass}; use crate::types::{SpecialFormType, Type, protocol_class::ProtocolClass};
use crate::util::diagnostics::format_enumeration; use crate::util::diagnostics::format_enumeration;
@ -2719,7 +2719,7 @@ pub(crate) fn report_undeclared_protocol_member(
if definition.kind(db).is_unannotated_assignment() { if definition.kind(db).is_unannotated_assignment() {
let binding_type = binding_type(db, definition); let binding_type = binding_type(db, definition);
let suggestion = binding_type.promote_literals(db); let suggestion = binding_type.promote_literals(db, TypeContext::default());
if should_give_hint(db, suggestion) { if should_give_hint(db, suggestion) {
diagnostic.set_primary_message(format_args!( diagnostic.set_primary_message(format_args!(
@ -2826,6 +2826,7 @@ pub(crate) fn report_invalid_or_unsupported_base(
db, db,
"__mro_entries__", "__mro_entries__",
CallArguments::positional([tuple_of_types]), CallArguments::positional([tuple_of_types]),
TypeContext::default(),
) { ) {
Ok(ret) => { Ok(ret) => {
if ret.return_type(db).is_assignable_to(db, tuple_of_types) { if ret.return_type(db).is_assignable_to(db, tuple_of_types) {

View File

@ -82,8 +82,8 @@ use crate::types::{
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase,
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor,
SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeMapping, TypeRelation, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext, TypeMapping,
UnionBuilder, binding_type, todo_type, walk_signature, TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature,
}; };
use crate::{Db, FxOrderSet, ModuleName, resolve_module}; use crate::{Db, FxOrderSet, ModuleName, resolve_module};
@ -667,14 +667,15 @@ impl<'db> FunctionType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
let updated_signature = let updated_signature =
self.signature(db) self.signature(db)
.apply_type_mapping_impl(db, type_mapping, visitor); .apply_type_mapping_impl(db, type_mapping, tcx, visitor);
let updated_last_definition_signature = self let updated_last_definition_signature = self
.last_definition_signature(db) .last_definition_signature(db)
.apply_type_mapping_impl(db, type_mapping, visitor); .apply_type_mapping_impl(db, type_mapping, tcx, visitor);
Self::new( Self::new(
db, db,
self.literal(db), self.literal(db),

View File

@ -16,7 +16,7 @@ use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor, ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
MaterializationKind, NormalizedVisitor, Type, TypeMapping, TypeRelation, MaterializationKind, NormalizedVisitor, Type, TypeContext, TypeMapping, TypeRelation,
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType,
binding_type, declaration_type, binding_type, declaration_type,
}; };
@ -460,8 +460,11 @@ impl<'db> GenericContext<'db> {
generic_context: self, generic_context: self,
types: &expanded[0..idx], types: &expanded[0..idx],
}; };
let default = let default = default.apply_type_mapping(
default.apply_type_mapping(db, &TypeMapping::PartialSpecialization(partial)); db,
&TypeMapping::PartialSpecialization(partial),
TypeContext::default(),
);
expanded[idx] = default; expanded[idx] = default;
} }
@ -791,27 +794,34 @@ impl<'db> Specialization<'db> {
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
) -> Self { ) -> Self {
self.apply_type_mapping_impl(db, type_mapping, &ApplyTypeMappingVisitor::default()) self.apply_type_mapping_impl(db, type_mapping, &[], &ApplyTypeMappingVisitor::default())
} }
pub(crate) fn apply_type_mapping_impl<'a>( pub(crate) fn apply_type_mapping_impl<'a>(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: &[Type<'db>],
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
if let TypeMapping::Materialize(materialization_kind) = type_mapping { if let TypeMapping::Materialize(materialization_kind) = type_mapping {
return self.materialize_impl(db, *materialization_kind, visitor); return self.materialize_impl(db, *materialization_kind, visitor);
} }
let types: Box<[_]> = self let types: Box<[_]> = self
.types(db) .types(db)
.iter() .iter()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)) .enumerate()
.map(|(i, ty)| {
let tcx = TypeContext::new(tcx.get(i).copied());
ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
})
.collect(); .collect();
let tuple_inner = self let tuple_inner = self.tuple_inner(db).and_then(|tuple| {
.tuple_inner(db) tuple.apply_type_mapping_impl(db, type_mapping, TypeContext::default(), visitor)
.and_then(|tuple| tuple.apply_type_mapping_impl(db, type_mapping, visitor)); });
Specialization::new( Specialization::new(
db, db,
self.generic_context(db), self.generic_context(db),
@ -924,6 +934,7 @@ impl<'db> Specialization<'db> {
tuple.apply_type_mapping_impl( tuple.apply_type_mapping_impl(
db, db,
&TypeMapping::Materialize(materialization_kind), &TypeMapping::Materialize(materialization_kind),
TypeContext::default(),
visitor, visitor,
) )
}); });
@ -1122,19 +1133,30 @@ impl<'db> SpecializationBuilder<'db> {
} }
} }
pub(crate) fn build(&mut self, generic_context: GenericContext<'db>) -> Specialization<'db> { pub(crate) fn build(
&mut self,
generic_context: GenericContext<'db>,
tcx: TypeContext<'db>,
) -> Specialization<'db> {
let tcx_specialization = tcx
.annotation
.and_then(|annotation| annotation.specialization_of(self.db, None));
let types = (generic_context.variables_inner(self.db).iter()).map(|(variable, options)| { let types = (generic_context.variables_inner(self.db).iter()).map(|(variable, options)| {
let mut ty = self.types.get(variable).copied(); let mut ty = self.types.get(variable).copied();
// When inferring a specialization for a generic class typevar from a constructor call, // When inferring a specialization for a generic class typevar from a constructor call,
// promote any typevars that are inferred as a literal to the corresponding instance // promote any typevars that are inferred as a literal to the corresponding instance type.
// type.
if options.should_promote_literals { if options.should_promote_literals {
ty = ty.map(|ty| ty.promote_literals(self.db)); let tcx = tcx_specialization
.and_then(|specialization| specialization.get(self.db, *variable));
ty = ty.map(|ty| ty.promote_literals(self.db, TypeContext::new(tcx)));
} }
ty ty
}); });
// TODO Infer the tuple spec for a tuple type // TODO Infer the tuple spec for a tuple type
generic_context.specialize_partial(self.db, types) generic_context.specialize_partial(self.db, types)
} }

View File

@ -383,11 +383,11 @@ impl<'db> TypeContext<'db> {
// specialization. // specialization.
fn known_specialization( fn known_specialization(
&self, &self,
known_class: KnownClass,
db: &'db dyn Db, db: &'db dyn Db,
known_class: KnownClass,
) -> Option<Specialization<'db>> { ) -> Option<Specialization<'db>> {
self.annotation self.annotation
.and_then(|ty| ty.known_specialization(known_class, db)) .and_then(|ty| ty.known_specialization(db, known_class))
} }
} }

View File

@ -3261,6 +3261,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db, db,
"__setitem__", "__setitem__",
CallArguments::positional([slice_ty, assigned_ty]), CallArguments::positional([slice_ty, assigned_ty]),
TypeContext::default(),
) { ) {
Ok(_) => true, Ok(_) => true,
Err(err) => match err { Err(err) => match err {
@ -3533,6 +3534,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db, db,
"__setattr__", "__setattr__",
&mut CallArguments::positional([Type::string_literal(db, attribute), value_ty]), &mut CallArguments::positional([Type::string_literal(db, attribute), value_ty]),
TypeContext::default(),
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK, MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
); );
@ -4239,6 +4241,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db, db,
op.in_place_dunder(), op.in_place_dunder(),
CallArguments::positional([value_type]), CallArguments::positional([value_type]),
TypeContext::default(),
); );
match call { match call {
@ -5451,7 +5454,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} = tuple; } = tuple;
let annotated_tuple = tcx let annotated_tuple = tcx
.known_specialization(KnownClass::Tuple, self.db()) .known_specialization(self.db(), KnownClass::Tuple)
.and_then(|specialization| { .and_then(|specialization| {
specialization specialization
.tuple(self.db()) .tuple(self.db())
@ -5586,14 +5589,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
Some((class_literal, generic_context.variables(self.db()))) Some((class_literal, generic_context.variables(self.db())))
}; };
let (class_literal, elt_tys) = elt_tys(collection_class).unwrap_or_else(|| { let Some((class_literal, elt_tys)) = elt_tys(collection_class) else {
let name = collection_class.name(self.db()); // Infer the element types without type context, and fallback to unknown for
panic!("Typeshed should always have a `{name}` class in `builtins.pyi`") // custom typesheds.
}); for elt in elts.flatten().flatten() {
self.get_or_infer_expression(elt, TypeContext::default());
}
return None;
};
// Extract the annotated type of `T`, if provided. // Extract the annotated type of `T`, if provided.
let annotated_elt_tys = tcx let annotated_elt_tys = tcx
.known_specialization(collection_class, self.db()) .known_specialization(self.db(), collection_class)
.map(|specialization| specialization.types(self.db())); .map(|specialization| specialization.types(self.db()));
// Create a set of constraints to infer a precise type for `T`. // Create a set of constraints to infer a precise type for `T`.
@ -5633,7 +5641,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// Merge the inferred type of the nested dictionary. // Merge the inferred type of the nested dictionary.
if let Some(specialization) = if let Some(specialization) =
inferred_value_ty.known_specialization(KnownClass::Dict, self.db()) inferred_value_ty.known_specialization(self.db(), KnownClass::Dict)
{ {
for (elt_ty, inferred_elt_ty) in for (elt_ty, inferred_elt_ty) in
iter::zip(elt_tys.clone(), specialization.types(self.db())) iter::zip(elt_tys.clone(), specialization.types(self.db()))
@ -5656,14 +5664,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// Convert any element literals to their promoted type form to avoid excessively large // Convert any element literals to their promoted type form to avoid excessively large
// unions for large nested list literals, which the constraint solver struggles with. // unions for large nested list literals, which the constraint solver struggles with.
let inferred_elt_ty = inferred_elt_ty.promote_literals(self.db()); let inferred_elt_ty = inferred_elt_ty.promote_literals(self.db(), elt_tcx);
builder.infer(Type::TypeVar(elt_ty), inferred_elt_ty).ok()?; builder.infer(Type::TypeVar(elt_ty), inferred_elt_ty).ok()?;
} }
} }
let class_type = class_literal let class_type = class_literal.apply_specialization(self.db(), |generic_context| {
.apply_specialization(self.db(), |generic_context| builder.build(generic_context)); builder.build(generic_context, TypeContext::default())
});
Type::from(class_type).to_instance(self.db()) Type::from(class_type).to_instance(self.db())
} }
@ -6186,7 +6195,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms); self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
return callable_type return callable_type
.try_call_constructor(self.db(), call_arguments) .try_call_constructor(self.db(), call_arguments, tcx)
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
err.report_diagnostic(&self.context, callable_type, call_expression.into()); err.report_diagnostic(&self.context, callable_type, call_expression.into());
err.return_type() err.return_type()
@ -7225,6 +7234,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.db(), self.db(),
unary_dunder_method, unary_dunder_method,
CallArguments::none(), CallArguments::none(),
TypeContext::default(),
) { ) {
Ok(outcome) => outcome.return_type(self.db()), Ok(outcome) => outcome.return_type(self.db()),
Err(e) => { Err(e) => {
@ -7644,6 +7654,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.db(), self.db(),
reflected_dunder, reflected_dunder,
CallArguments::positional([left_ty]), CallArguments::positional([left_ty]),
TypeContext::default(),
) )
.map(|outcome| outcome.return_type(self.db())) .map(|outcome| outcome.return_type(self.db()))
.or_else(|_| { .or_else(|_| {
@ -7652,6 +7663,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.db(), self.db(),
op.dunder(), op.dunder(),
CallArguments::positional([right_ty]), CallArguments::positional([right_ty]),
TypeContext::default(),
) )
.map(|outcome| outcome.return_type(self.db())) .map(|outcome| outcome.return_type(self.db()))
}) })
@ -7664,6 +7676,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.db(), self.db(),
op.dunder(), op.dunder(),
CallArguments::positional([right_ty]), CallArguments::positional([right_ty]),
TypeContext::default(),
) )
.map(|outcome| outcome.return_type(self.db())) .map(|outcome| outcome.return_type(self.db()))
.ok(); .ok();
@ -7677,6 +7690,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.db(), self.db(),
op.reflected_dunder(), op.reflected_dunder(),
CallArguments::positional([left_ty]), CallArguments::positional([left_ty]),
TypeContext::default(),
) )
.map(|outcome| outcome.return_type(self.db())) .map(|outcome| outcome.return_type(self.db()))
.ok() .ok()
@ -8430,6 +8444,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
db, db,
op.dunder(), op.dunder(),
&mut CallArguments::positional([right]), &mut CallArguments::positional([right]),
TypeContext::default(),
policy, policy,
) )
.map(|outcome| outcome.return_type(db)) .map(|outcome| outcome.return_type(db))
@ -9025,7 +9040,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// If the class defines `__getitem__`, return its return type. // If the class defines `__getitem__`, return its return type.
// //
// See: https://docs.python.org/3/reference/datamodel.html#class-getitem-versus-getitem // See: https://docs.python.org/3/reference/datamodel.html#class-getitem-versus-getitem
match value_ty.try_call_dunder(db, "__getitem__", CallArguments::positional([slice_ty])) { match value_ty.try_call_dunder(
db,
"__getitem__",
CallArguments::positional([slice_ty]),
TypeContext::default(),
) {
Ok(outcome) => { Ok(outcome) => {
return outcome.return_type(db); return outcome.return_type(db);
} }

View File

@ -14,8 +14,8 @@ use crate::types::protocol_class::walk_protocol_interface;
use crate::types::tuple::{TupleSpec, TupleType}; use crate::types::tuple::{TupleSpec, TupleType};
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, ClassBase, ClassLiteral, FindLegacyTypeVarsVisitor, ApplyTypeMappingVisitor, ClassBase, ClassLiteral, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeMapping, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeContext,
TypeRelation, VarianceInferable, TypeMapping, TypeRelation, VarianceInferable,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
@ -475,15 +475,16 @@ impl<'db> NominalInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Type<'db> { ) -> Type<'db> {
match self.0 { match self.0 {
NominalInstanceInner::ExactTuple(tuple) => { NominalInstanceInner::ExactTuple(tuple) => {
Type::tuple(tuple.apply_type_mapping_impl(db, type_mapping, visitor)) Type::tuple(tuple.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
NominalInstanceInner::NonTuple(class) => Type::non_tuple_instance( NominalInstanceInner::NonTuple(class) => Type::non_tuple_instance(
db, db,
class.apply_type_mapping_impl(db, type_mapping, visitor), class.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
), ),
NominalInstanceInner::Object => Type::object(), NominalInstanceInner::Object => Type::object(),
} }
@ -734,15 +735,16 @@ impl<'db> ProtocolInstanceType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
match self.inner { match self.inner {
Protocol::FromClass(class) => { Protocol::FromClass(class) => {
Self::from_class(class.apply_type_mapping_impl(db, type_mapping, visitor)) Self::from_class(class.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
}
Protocol::Synthesized(synthesized) => {
Self::synthesized(synthesized.apply_type_mapping_impl(db, type_mapping, visitor))
} }
Protocol::Synthesized(synthesized) => Self::synthesized(
synthesized.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
),
} }
} }
@ -813,7 +815,7 @@ mod synthesized_protocol {
use crate::types::protocol_class::ProtocolInterface; use crate::types::protocol_class::ProtocolInterface;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor,
NormalizedVisitor, TypeMapping, TypeVarVariance, VarianceInferable, NormalizedVisitor, TypeContext, TypeMapping, TypeVarVariance, VarianceInferable,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
@ -844,9 +846,10 @@ mod synthesized_protocol {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
_visitor: &ApplyTypeMappingVisitor<'db>, _visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
Self(self.0.specialized_and_normalized(db, type_mapping)) Self(self.0.specialized_and_normalized(db, type_mapping, tcx))
} }
pub(super) fn find_legacy_typevars_impl( pub(super) fn find_legacy_typevars_impl(

View File

@ -6,6 +6,7 @@ use itertools::Itertools;
use ruff_python_ast::name::Name; use ruff_python_ast::name::Name;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::types::TypeContext;
use crate::{ use crate::{
Db, FxOrderSet, Db, FxOrderSet,
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations}, place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
@ -339,6 +340,7 @@ impl<'db> ProtocolInterface<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
) -> Self { ) -> Self {
Self::new( Self::new(
db, db,
@ -350,6 +352,7 @@ impl<'db> ProtocolInterface<'db> {
data.apply_type_mapping_impl( data.apply_type_mapping_impl(
db, db,
type_mapping, type_mapping,
tcx,
&ApplyTypeMappingVisitor::default(), &ApplyTypeMappingVisitor::default(),
) )
.normalized(db), .normalized(db),
@ -428,10 +431,13 @@ impl<'db> ProtocolMemberData<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
Self { Self {
kind: self.kind.apply_type_mapping_impl(db, type_mapping, visitor), kind: self
.kind
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
qualifiers: self.qualifiers, qualifiers: self.qualifiers,
} }
} }
@ -516,18 +522,22 @@ impl<'db> ProtocolMemberKind<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
match self { match self {
ProtocolMemberKind::Method(callable) => ProtocolMemberKind::Method( ProtocolMemberKind::Method(callable) => ProtocolMemberKind::Method(
callable.apply_type_mapping_impl(db, type_mapping, visitor), callable.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
), ),
ProtocolMemberKind::Property(property) => ProtocolMemberKind::Property( ProtocolMemberKind::Property(property) => ProtocolMemberKind::Property(
property.apply_type_mapping_impl(db, type_mapping, visitor), property.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
), ),
ProtocolMemberKind::Other(ty) => { ProtocolMemberKind::Other(ty) => ProtocolMemberKind::Other(ty.apply_type_mapping_impl(
ProtocolMemberKind::Other(ty.apply_type_mapping_impl(db, type_mapping, visitor)) db,
} type_mapping,
tcx,
visitor,
)),
} }
} }

View File

@ -28,7 +28,7 @@ use crate::types::infer::nearest_enclosing_class;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor, ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind,
NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable, todo_type, NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, VarianceInferable, 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};
@ -138,12 +138,13 @@ impl<'db> CallableSignature<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
Self::from_overloads( Self::from_overloads(
self.overloads self.overloads
.iter() .iter()
.map(|signature| signature.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|signature| signature.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
) )
} }
@ -435,6 +436,7 @@ impl<'db> Signature<'db> {
.apply_type_mapping( .apply_type_mapping(
db, db,
&TypeMapping::MarkTypeVarsInferable(Some(definition.into())), &TypeMapping::MarkTypeVarsInferable(Some(definition.into())),
TypeContext::default(),
); );
if function_node.is_async && !is_generator { if function_node.is_async && !is_generator {
KnownClass::CoroutineType KnownClass::CoroutineType
@ -523,6 +525,7 @@ impl<'db> Signature<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
let flipped_mapping = match type_mapping { let flipped_mapping = match type_mapping {
@ -538,10 +541,10 @@ impl<'db> Signature<'db> {
definition: self.definition, definition: self.definition,
parameters: self parameters: self
.parameters .parameters
.apply_type_mapping_impl(db, flipped_mapping, visitor), .apply_type_mapping_impl(db, flipped_mapping, tcx, visitor),
return_ty: self return_ty: self
.return_ty .return_ty
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
} }
} }
@ -582,10 +585,16 @@ impl<'db> Signature<'db> {
parameters = parameters.apply_type_mapping_impl( parameters = parameters.apply_type_mapping_impl(
db, db,
&TypeMapping::BindSelf(self_type), &TypeMapping::BindSelf(self_type),
TypeContext::default(),
&ApplyTypeMappingVisitor::default(), &ApplyTypeMappingVisitor::default(),
); );
return_ty = return_ty = return_ty.map(|ty| {
return_ty.map(|ty| ty.apply_type_mapping(db, &TypeMapping::BindSelf(self_type))); ty.apply_type_mapping(
db,
&TypeMapping::BindSelf(self_type),
TypeContext::default(),
)
});
} }
Self { Self {
generic_context: self.generic_context, generic_context: self.generic_context,
@ -1294,6 +1303,7 @@ impl<'db> Parameters<'db> {
.expect("We should always find the surrounding class for an implicit self: Self annotation").apply_type_mapping( .expect("We should always find the surrounding class for an implicit self: Self annotation").apply_type_mapping(
db, db,
&TypeMapping::MarkTypeVarsInferable(None), &TypeMapping::MarkTypeVarsInferable(None),
TypeContext::default()
) )
) )
} else { } else {
@ -1423,6 +1433,7 @@ impl<'db> Parameters<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
match type_mapping { match type_mapping {
@ -1442,7 +1453,7 @@ impl<'db> Parameters<'db> {
value: self value: self
.value .value
.iter() .iter()
.map(|param| param.apply_type_mapping_impl(db, type_mapping, visitor)) .map(|param| param.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
.collect(), .collect(),
is_gradual: self.is_gradual, is_gradual: self.is_gradual,
}, },
@ -1634,13 +1645,16 @@ impl<'db> Parameter<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
Self { Self {
annotated_type: self annotated_type: self
.annotated_type .annotated_type
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
kind: self.kind.apply_type_mapping_impl(db, type_mapping, visitor), kind: self
.kind
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
inferred_annotation: self.inferred_annotation, inferred_annotation: self.inferred_annotation,
form: self.form, form: self.form,
} }
@ -1718,6 +1732,7 @@ impl<'db> Parameter<'db> {
definition_expression_type(db, definition, annotation).apply_type_mapping( definition_expression_type(db, definition, annotation).apply_type_mapping(
db, db,
&TypeMapping::MarkTypeVarsInferable(Some(definition.into())), &TypeMapping::MarkTypeVarsInferable(Some(definition.into())),
TypeContext::default(),
) )
}), }),
kind, kind,
@ -1857,25 +1872,26 @@ impl<'db> ParameterKind<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> 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
.as_ref() .as_ref()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
name: name.clone(), name: name.clone(),
}, },
Self::PositionalOrKeyword { default_type, name } => Self::PositionalOrKeyword { Self::PositionalOrKeyword { default_type, name } => Self::PositionalOrKeyword {
default_type: default_type default_type: default_type
.as_ref() .as_ref()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
name: name.clone(), name: name.clone(),
}, },
Self::KeywordOnly { default_type, name } => Self::KeywordOnly { Self::KeywordOnly { default_type, name } => Self::KeywordOnly {
default_type: default_type default_type: default_type
.as_ref() .as_ref()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
name: name.clone(), name: name.clone(),
}, },
Self::Variadic { .. } | Self::KeywordVariadic { .. } => self.clone(), Self::Variadic { .. } | Self::KeywordVariadic { .. } => self.clone(),

View File

@ -5,8 +5,8 @@ use crate::types::variance::VarianceInferable;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType, ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType,
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, KnownClass, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, KnownClass,
MaterializationKind, MemberLookupPolicy, NormalizedVisitor, SpecialFormType, Type, TypeMapping, MaterializationKind, MemberLookupPolicy, NormalizedVisitor, SpecialFormType, Type, TypeContext,
TypeRelation, TypeMapping, TypeRelation,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
@ -84,6 +84,7 @@ impl<'db> SubclassOfType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Type<'db> { ) -> Type<'db> {
match self.subclass_of { match self.subclass_of {
@ -91,6 +92,7 @@ impl<'db> SubclassOfType<'db> {
subclass_of: SubclassOfInner::Class(class.apply_type_mapping_impl( subclass_of: SubclassOfInner::Class(class.apply_type_mapping_impl(
db, db,
type_mapping, type_mapping,
tcx,
visitor, visitor,
)), )),
}), }),

View File

@ -22,7 +22,6 @@ use std::hash::Hash;
use itertools::{Either, EitherOrBoth, Itertools}; use itertools::{Either, EitherOrBoth, Itertools};
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::types::Truthiness;
use crate::types::class::{ClassType, KnownClass}; use crate::types::class::{ClassType, KnownClass};
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
use crate::types::{ use crate::types::{
@ -30,6 +29,7 @@ use crate::types::{
IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation,
UnionBuilder, UnionType, UnionBuilder, UnionType,
}; };
use crate::types::{Truthiness, TypeContext};
use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError}; use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
use crate::{Db, FxOrderSet, Program}; use crate::{Db, FxOrderSet, Program};
@ -232,13 +232,14 @@ impl<'db> TupleType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Option<Self> { ) -> Option<Self> {
TupleType::new( TupleType::new(
db, db,
&self &self
.tuple(db) .tuple(db)
.apply_type_mapping_impl(db, type_mapping, visitor), .apply_type_mapping_impl(db, type_mapping, tcx, visitor),
) )
} }
@ -396,12 +397,32 @@ impl<'db> FixedLengthTuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
let tcx_tuple = tcx
.annotation
.and_then(|annotation| annotation.known_specialization(db, KnownClass::Tuple))
.and_then(|specialization| {
specialization
.tuple(db)
.expect("the specialization of `KnownClass::Tuple` must have a tuple spec")
.resize(db, TupleLength::Fixed(self.0.len()))
.ok()
});
let tcx_elements = match tcx_tuple.as_ref() {
None => Either::Right(std::iter::repeat(TypeContext::default())),
Some(tuple) => {
Either::Left(tuple.all_elements().map(|tcx| TypeContext::new(Some(*tcx))))
}
};
Self::from_elements( Self::from_elements(
self.0 self.0
.iter() .iter()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .zip(tcx_elements)
.map(|(ty, tcx)| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
) )
} }
@ -736,17 +757,18 @@ impl<'db> VariableLengthTuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> TupleSpec<'db> { ) -> TupleSpec<'db> {
Self::mixed( Self::mixed(
self.prefix self.prefix
.iter() .iter()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
self.variable self.variable
.apply_type_mapping_impl(db, type_mapping, visitor), .apply_type_mapping_impl(db, type_mapping, tcx, visitor),
self.suffix self.suffix
.iter() .iter()
.map(|ty| ty.apply_type_mapping_impl(db, type_mapping, visitor)), .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
) )
} }
@ -1116,13 +1138,14 @@ impl<'db> Tuple<Type<'db>> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
match self { match self {
Tuple::Fixed(tuple) => { Tuple::Fixed(tuple) => {
Tuple::Fixed(tuple.apply_type_mapping_impl(db, type_mapping, visitor)) Tuple::Fixed(tuple.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
} }
Tuple::Variable(tuple) => tuple.apply_type_mapping_impl(db, type_mapping, visitor), Tuple::Variable(tuple) => tuple.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
} }
} }

View File

@ -12,6 +12,7 @@ use super::diagnostic::{
report_missing_typed_dict_key, report_missing_typed_dict_key,
}; };
use super::{ApplyTypeMappingVisitor, Type, TypeMapping, visitor}; use super::{ApplyTypeMappingVisitor, Type, TypeMapping, visitor};
use crate::types::TypeContext;
use crate::{Db, FxOrderMap}; use crate::{Db, FxOrderMap};
use ordermap::OrderSet; use ordermap::OrderSet;
@ -62,13 +63,17 @@ impl<'db> TypedDictType<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>, type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>, visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self { ) -> Self {
// TODO: Materialization of gradual TypedDicts needs more logic // TODO: Materialization of gradual TypedDicts needs more logic
Self { Self {
defining_class: self defining_class: self.defining_class.apply_type_mapping_impl(
.defining_class db,
.apply_type_mapping_impl(db, type_mapping, visitor), type_mapping,
tcx,
visitor,
),
} }
} }
} }