[ty] Handle typevars that have other typevars as a default (#17956)

It's possible for a typevar to list another typevar as its default
value:

```py
class C[T, U = T]: ...
```

When specializing this class, if a type isn't provided for `U`, we would
previously use the default as-is, leaving an unspecialized `T` typevar
in the specialization. Instead, we want to use what `T` is mapped to as
the type of `U`.

```py
reveal_type(C())  # revealed: C[Unknown, Unknown]
reveal_type(C[int]())  # revealed: C[int, int]
reveal_type(C[int, str]())  # revealed: C[int, str]
```

This is especially important for the `slice` built-in type.
This commit is contained in:
Douglas Creager 2025-05-08 19:01:27 -04:00 committed by GitHub
parent f51f1f7153
commit b705664d49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 226 additions and 102 deletions

View file

@ -1945,7 +1945,7 @@ reveal_type(C.a_complex) # revealed: int | float | complex
reveal_type(C.a_tuple) # revealed: tuple[int] reveal_type(C.a_tuple) # revealed: tuple[int]
reveal_type(C.a_range) # revealed: range reveal_type(C.a_range) # revealed: range
# TODO: revealed: slice[Any, Literal[1], Any] # TODO: revealed: slice[Any, Literal[1], Any]
reveal_type(C.a_slice) # revealed: slice[Any, _StartT_co, _StartT_co | _StopT_co] reveal_type(C.a_slice) # revealed: slice[Any, Any, Any]
reveal_type(C.a_type) # revealed: type reveal_type(C.a_type) # revealed: type
reveal_type(C.a_none) # revealed: None reveal_type(C.a_none) # revealed: None
``` ```

View file

@ -86,6 +86,26 @@ S = TypeVar("S")
reveal_type(S.__default__) # revealed: NoDefault reveal_type(S.__default__) # revealed: NoDefault
``` ```
### Using other typevars as a default
```py
from typing import Generic, TypeVar, Union
T = TypeVar("T")
U = TypeVar("U", default=T)
V = TypeVar("V", default=Union[T, U])
class Valid(Generic[T, U, V]): ...
reveal_type(Valid()) # revealed: Valid[Unknown, Unknown, Unknown]
reveal_type(Valid[int]()) # revealed: Valid[int, int, int]
reveal_type(Valid[int, str]()) # revealed: Valid[int, str, int | str]
reveal_type(Valid[int, str, None]()) # revealed: Valid[int, str, None]
# TODO: error, default value for U isn't available in the generic context
class Invalid(Generic[U]): ...
```
### Type variables with an upper bound ### Type variables with an upper bound
```py ```py

View file

@ -40,6 +40,25 @@ def g[S]():
reveal_type(S.__default__) # revealed: NoDefault reveal_type(S.__default__) # revealed: NoDefault
``` ```
### Using other typevars as a default
```toml
[environment]
python-version = "3.13"
```
```py
class Valid[T, U = T, V = T | U]: ...
reveal_type(Valid()) # revealed: Valid[Unknown, Unknown, Unknown]
reveal_type(Valid[int]()) # revealed: Valid[int, int, int]
reveal_type(Valid[int, str]()) # revealed: Valid[int, str, int | str]
reveal_type(Valid[int, str, None]()) # revealed: Valid[int, str, None]
# error: [unresolved-reference]
class Invalid[S = T]: ...
```
### Type variables with an upper bound ### Type variables with an upper bound
```py ```py

View file

@ -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}; use crate::types::generics::{GenericContext, Specialization, TypeMapping};
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,13 +342,13 @@ pub struct PropertyInstanceType<'db> {
} }
impl<'db> PropertyInstanceType<'db> { impl<'db> PropertyInstanceType<'db> {
fn apply_specialization(self, db: &'db dyn Db, specialization: Specialization<'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_specialization(db, specialization)); .map(|ty| ty.apply_type_mapping(db, type_mapping));
let setter = self let setter = self
.setter(db) .setter(db)
.map(|ty| ty.apply_specialization(db, specialization)); .map(|ty| ty.apply_type_mapping(db, type_mapping));
Self::new(db, getter, setter) Self::new(db, getter, setter)
} }
@ -5043,75 +5043,83 @@ impl<'db> Type<'db> {
self, self,
db: &'db dyn Db, db: &'db dyn Db,
specialization: Specialization<'db>, specialization: Specialization<'db>,
) -> Type<'db> {
self.apply_type_mapping(db, specialization.type_mapping())
}
fn apply_type_mapping<'a>(
self,
db: &'db dyn Db,
type_mapping: TypeMapping<'a, 'db>,
) -> Type<'db> { ) -> Type<'db> {
match self { match self {
Type::TypeVar(typevar) => specialization.get(db, typevar).unwrap_or(self), Type::TypeVar(typevar) => type_mapping.get(db, typevar).unwrap_or(self),
Type::FunctionLiteral(function) => { Type::FunctionLiteral(function) => {
Type::FunctionLiteral(function.apply_specialization(db, specialization)) Type::FunctionLiteral(function.apply_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_specialization(db, specialization), method.function(db).apply_type_mapping(db, type_mapping),
method.self_instance(db).apply_specialization(db, specialization), method.self_instance(db).apply_type_mapping(db, type_mapping),
)), )),
Type::NominalInstance(instance) => Type::NominalInstance( Type::NominalInstance(instance) => Type::NominalInstance(
instance.apply_specialization(db, specialization), instance.apply_type_mapping(db, type_mapping),
), ),
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => { Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet( Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(
function.apply_specialization(db, specialization), function.apply_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_specialization(db, specialization), function.apply_type_mapping(db, type_mapping),
)) ))
} }
Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(property)) => { Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(property)) => {
Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet( Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(
property.apply_specialization(db, specialization), property.apply_type_mapping(db, type_mapping),
)) ))
} }
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(property)) => { Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(property)) => {
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet( Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(
property.apply_specialization(db, specialization), property.apply_type_mapping(db, type_mapping),
)) ))
} }
Type::Callable(callable) => { Type::Callable(callable) => {
Type::Callable(callable.apply_specialization(db, specialization)) Type::Callable(callable.apply_type_mapping(db, type_mapping))
} }
Type::GenericAlias(generic) => { Type::GenericAlias(generic) => {
let specialization = generic let specialization = generic
.specialization(db) .specialization(db)
.apply_specialization(db, specialization); .apply_type_mapping(db, type_mapping);
Type::GenericAlias(GenericAlias::new(db, generic.origin(db), specialization)) Type::GenericAlias(GenericAlias::new(db, generic.origin(db), specialization))
} }
Type::PropertyInstance(property) => { Type::PropertyInstance(property) => {
Type::PropertyInstance(property.apply_specialization(db, specialization)) Type::PropertyInstance(property.apply_type_mapping(db, type_mapping))
} }
Type::Union(union) => union.map(db, |element| { Type::Union(union) => union.map(db, |element| {
element.apply_specialization(db, specialization) element.apply_type_mapping(db, type_mapping)
}), }),
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_specialization(db, specialization)); builder.add_positive(positive.apply_type_mapping(db, type_mapping));
} }
for negative in intersection.negative(db) { for negative in intersection.negative(db) {
builder = builder =
builder.add_negative(negative.apply_specialization(db, specialization)); builder.add_negative(negative.apply_type_mapping(db, type_mapping));
} }
builder.build() builder.build()
} }
@ -5119,7 +5127,7 @@ impl<'db> Type<'db> {
db, db,
tuple tuple
.iter(db) .iter(db)
.map(|ty| ty.apply_specialization(db, specialization)), .map(|ty| ty.apply_type_mapping(db, type_mapping)),
), ),
Type::Dynamic(_) Type::Dynamic(_)
@ -6844,6 +6852,10 @@ impl<'db> FunctionType<'db> {
) )
} }
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,
@ -7192,15 +7204,12 @@ impl<'db> CallableType<'db> {
) )
} }
/// Apply a specialization to this callable type. fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
///
/// See [`Type::apply_specialization`] for more details.
fn apply_specialization(self, db: &'db dyn Db, specialization: Specialization<'db>) -> Self {
CallableType::from_overloads( CallableType::from_overloads(
db, db,
self.signatures(db) self.signatures(db)
.iter() .iter()
.map(|signature| signature.apply_specialization(db, specialization)), .map(|signature| signature.apply_type_mapping(db, type_mapping)),
) )
} }

View file

@ -1368,21 +1368,6 @@ impl<'db> Binding<'db> {
&self.parameter_tys &self.parameter_tys
} }
/// Returns the bound types for each parameter, in parameter source order, with default values
/// applied for arguments that weren't matched to a parameter. Returns `None` if there are any
/// non-default arguments that weren't matched to a parameter.
pub(crate) fn parameter_types_with_defaults(
&self,
signature: &Signature<'db>,
) -> Option<Box<[Type<'db>]>> {
signature
.parameters()
.iter()
.zip(&self.parameter_tys)
.map(|(parameter, parameter_ty)| parameter_ty.or(parameter.default_type()))
.collect()
}
pub(crate) fn arguments_for_parameter<'a>( pub(crate) fn arguments_for_parameter<'a>(
&'a self, &'a self,
argument_types: &'a CallArgumentTypes<'a, 'db>, argument_types: &'a CallArgumentTypes<'a, 'db>,

View file

@ -8,7 +8,7 @@ use super::{
}; };
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::semantic_index::DeclarationWithConstraint; use crate::semantic_index::DeclarationWithConstraint;
use crate::types::generics::{GenericContext, Specialization}; use crate::types::generics::{GenericContext, Specialization, TypeMapping};
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,
@ -147,16 +147,11 @@ impl<'db> GenericAlias<'db> {
self.origin(db).definition(db) self.origin(db).definition(db)
} }
pub(super) fn apply_specialization( fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
self,
db: &'db dyn Db,
specialization: Specialization<'db>,
) -> Self {
Self::new( Self::new(
db, db,
self.origin(db), self.origin(db),
self.specialization(db) self.specialization(db).apply_type_mapping(db, type_mapping),
.apply_specialization(db, specialization),
) )
} }
} }
@ -236,16 +231,14 @@ impl<'db> ClassType<'db> {
self.is_known(db, KnownClass::Object) self.is_known(db, KnownClass::Object)
} }
pub(super) fn apply_specialization( pub(super) fn apply_type_mapping<'a>(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
specialization: Specialization<'db>, type_mapping: TypeMapping<'a, '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(db, type_mapping)),
Self::Generic(generic.apply_specialization(db, specialization))
}
} }
} }

View file

@ -1,4 +1,4 @@
use crate::types::generics::{GenericContext, Specialization}; use crate::types::generics::{GenericContext, Specialization, TypeMapping};
use crate::types::{ use crate::types::{
todo_type, ClassType, DynamicType, KnownClass, KnownInstanceType, MroIterator, Type, todo_type, ClassType, DynamicType, KnownClass, KnownInstanceType, MroIterator, Type,
}; };
@ -215,13 +215,9 @@ impl<'db> ClassBase<'db> {
} }
} }
pub(crate) fn apply_specialization( fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
self,
db: &'db dyn Db,
specialization: Specialization<'db>,
) -> Self {
match self { match self {
Self::Class(class) => Self::Class(class.apply_specialization(db, specialization)), 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,
} }
} }
@ -232,7 +228,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_specialization(db, specialization) self.apply_type_mapping(db, specialization.type_mapping())
} else { } else {
self self
} }

View file

@ -130,12 +130,7 @@ impl<'db> GenericContext<'db> {
} }
pub(crate) fn default_specialization(self, db: &'db dyn Db) -> Specialization<'db> { pub(crate) fn default_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
let types = self self.specialize_partial(db, &vec![None; self.variables(db).len()])
.variables(db)
.iter()
.map(|typevar| typevar.default_ty(db).unwrap_or(Type::unknown()))
.collect();
self.specialize(db, types)
} }
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> { pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
@ -157,7 +152,9 @@ impl<'db> GenericContext<'db> {
} }
/// Creates a specialization of this generic context. Panics if the length of `types` does not /// Creates a specialization of this generic context. Panics if the length of `types` does not
/// match the number of typevars in the generic context. /// match the number of typevars in the generic context. You must provide a specific type for
/// each typevar; no defaults are used. (Use [`specialize_partial`](Self::specialize_partial)
/// if you might not have types for every typevar.)
pub(crate) fn specialize( pub(crate) fn specialize(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
@ -166,6 +163,50 @@ impl<'db> GenericContext<'db> {
assert!(self.variables(db).len() == types.len()); assert!(self.variables(db).len() == types.len());
Specialization::new(db, self, types) Specialization::new(db, self, types)
} }
/// Creates a specialization of this generic context. Panics if the length of `types` does not
/// match the number of typevars in the generic context. If any provided type is `None`, we
/// will use the corresponding typevar's default type.
pub(crate) fn specialize_partial(
self,
db: &'db dyn Db,
types: &[Option<Type<'db>>],
) -> Specialization<'db> {
let variables = self.variables(db);
assert!(variables.len() == types.len());
// Typevars can have other typevars as their default values, e.g.
//
// ```py
// class C[T, U = T]: ...
// ```
//
// If there is a mapping for `T`, we want to map `U` to that type, not to `T`. To handle
// this, we repeatedly apply the specialization to itself, until we reach a fixed point.
let mut expanded = vec![Type::unknown(); types.len()];
for (idx, (ty, typevar)) in types.iter().zip(variables).enumerate() {
if let Some(ty) = ty {
expanded[idx] = *ty;
continue;
}
let Some(default) = typevar.default_ty(db) else {
continue;
};
// 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
// requirement for legacy contexts.)
let type_mapping = TypeMapping::Partial {
generic_context: self,
types: &expanded[0..idx],
};
let default = default.apply_type_mapping(db, type_mapping);
expanded[idx] = default;
}
Specialization::new(db, self, expanded.into_boxed_slice())
}
} }
/// An assignment of a specific type to each type variable in a generic scope. /// An assignment of a specific type to each type variable in a generic scope.
@ -180,6 +221,10 @@ pub struct Specialization<'db> {
} }
impl<'db> Specialization<'db> { impl<'db> Specialization<'db> {
pub(crate) fn type_mapping(self) -> TypeMapping<'db, 'db> {
TypeMapping::Specialization(self)
}
/// 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
/// class inherits from a generic alias: /// class inherits from a generic alias:
/// ///
@ -194,10 +239,18 @@ 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())
}
pub(crate) fn apply_type_mapping<'a>(
self,
db: &'db dyn Db,
type_mapping: TypeMapping<'a, 'db>,
) -> Self {
let types: Box<[_]> = self let types: Box<[_]> = self
.types(db) .types(db)
.into_iter() .into_iter()
.map(|ty| ty.apply_specialization(db, other)) .map(|ty| ty.apply_type_mapping(db, type_mapping))
.collect(); .collect();
Specialization::new(db, self.generic_context(db), types) Specialization::new(db, self.generic_context(db), types)
} }
@ -244,16 +297,6 @@ impl<'db> Specialization<'db> {
Self::new(db, self.generic_context(db), types) Self::new(db, self.generic_context(db), types)
} }
/// Returns the type that a typevar is specialized to, or None if the typevar isn't part of
/// this specialization.
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)?;
Some(self.types(db)[index])
}
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, other: Specialization<'db>) -> bool { pub(crate) fn is_subtype_of(self, db: &'db dyn Db, other: Specialization<'db>) -> bool {
let generic_context = self.generic_context(db); let generic_context = self.generic_context(db);
if generic_context != other.generic_context(db) { if generic_context != other.generic_context(db) {
@ -403,6 +446,57 @@ impl<'db> Specialization<'db> {
} }
} }
/// A mapping between type variables and types.
///
/// 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`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(crate) enum TypeMapping<'a, 'db> {
Specialization(Specialization<'db>),
Partial {
generic_context: GenericContext<'db>,
types: &'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,
}
}
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
/// 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)?;
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> {
match self {
Self::Specialization(specialization) => specialization,
Self::Partial {
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())
}
}
}
}
/// Performs type inference between parameter annotations and argument types, producing a /// Performs type inference between parameter annotations and argument types, producing a
/// specialization of a generic function. /// specialization of a generic function.
pub(crate) struct SpecializationBuilder<'db> { pub(crate) struct SpecializationBuilder<'db> {

View file

@ -6899,8 +6899,10 @@ impl<'db> TypeInferenceBuilder<'db> {
} }
_ => CallArgumentTypes::positional([self.infer_type_expression(slice_node)]), _ => CallArgumentTypes::positional([self.infer_type_expression(slice_node)]),
}; };
let signature = generic_context.signature(self.db()); let signatures = Signatures::single(CallableSignature::single(
let signatures = Signatures::single(CallableSignature::single(value_ty, signature.clone())); value_ty,
generic_context.signature(self.db()),
));
let bindings = match Bindings::match_parameters(signatures, &call_argument_types) let bindings = match Bindings::match_parameters(signatures, &call_argument_types)
.check_types(self.db(), &call_argument_types) .check_types(self.db(), &call_argument_types)
{ {
@ -6918,10 +6920,8 @@ impl<'db> TypeInferenceBuilder<'db> {
.matching_overloads() .matching_overloads()
.next() .next()
.expect("valid bindings should have matching overload"); .expect("valid bindings should have matching overload");
let parameters = overload let specialization =
.parameter_types_with_defaults(&signature) generic_context.specialize_partial(self.db(), overload.parameter_types());
.expect("matching overload should not have missing arguments");
let specialization = generic_context.specialize(self.db(), parameters);
Type::from(GenericAlias::new(self.db(), generic_class, specialization)) Type::from(GenericAlias::new(self.db(), generic_class, specialization))
} }

View file

@ -3,7 +3,7 @@
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::Specialization; use crate::types::generics::TypeMapping;
use crate::Db; use crate::Db;
pub(super) use synthesized_protocol::SynthesizedProtocolType; pub(super) use synthesized_protocol::SynthesizedProtocolType;
@ -113,13 +113,13 @@ impl<'db> NominalInstanceType<'db> {
SubclassOfType::from(db, self.class) SubclassOfType::from(db, self.class)
} }
pub(super) fn apply_specialization( pub(super) fn apply_type_mapping<'a>(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
specialization: Specialization<'db>, type_mapping: TypeMapping<'a, 'db>,
) -> Self { ) -> Self {
Self { Self {
class: self.class.apply_specialization(db, specialization), class: self.class.apply_type_mapping(db, type_mapping),
} }
} }
} }

View file

@ -17,7 +17,7 @@ use smallvec::{smallvec, SmallVec};
use super::{definition_expression_type, DynamicType, Type}; use super::{definition_expression_type, DynamicType, Type};
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::Definition;
use crate::types::generics::{GenericContext, Specialization}; use crate::types::generics::{GenericContext, Specialization, TypeMapping};
use crate::types::{todo_type, TypeVarInstance}; use crate::types::{todo_type, TypeVarInstance};
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,14 +313,22 @@ impl<'db> Signature<'db> {
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
specialization: Specialization<'db>, specialization: Specialization<'db>,
) -> Self {
self.apply_type_mapping(db, specialization.type_mapping())
}
pub(crate) fn apply_type_mapping<'a>(
&self,
db: &'db dyn Db,
type_mapping: TypeMapping<'a, 'db>,
) -> Self { ) -> Self {
Self { Self {
generic_context: self.generic_context, generic_context: self.generic_context,
inherited_generic_context: self.inherited_generic_context, inherited_generic_context: self.inherited_generic_context,
parameters: self.parameters.apply_specialization(db, specialization), parameters: self.parameters.apply_type_mapping(db, type_mapping),
return_ty: self return_ty: self
.return_ty .return_ty
.map(|ty| ty.apply_specialization(db, specialization)), .map(|ty| ty.apply_type_mapping(db, type_mapping)),
} }
} }
@ -1053,12 +1061,12 @@ impl<'db> Parameters<'db> {
) )
} }
fn apply_specialization(&self, db: &'db dyn Db, specialization: Specialization<'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
.iter() .iter()
.map(|param| param.apply_specialization(db, specialization)) .map(|param| param.apply_type_mapping(db, type_mapping))
.collect(), .collect(),
is_gradual: self.is_gradual, is_gradual: self.is_gradual,
} }
@ -1225,12 +1233,12 @@ impl<'db> Parameter<'db> {
self self
} }
fn apply_specialization(&self, db: &'db dyn Db, specialization: Specialization<'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
.map(|ty| ty.apply_specialization(db, specialization)), .map(|ty| ty.apply_type_mapping(db, type_mapping)),
kind: self.kind.apply_specialization(db, specialization), kind: self.kind.apply_type_mapping(db, type_mapping),
form: self.form, form: self.form,
} }
} }
@ -1422,24 +1430,24 @@ pub(crate) enum ParameterKind<'db> {
} }
impl<'db> ParameterKind<'db> { impl<'db> ParameterKind<'db> {
fn apply_specialization(&self, db: &'db dyn Db, specialization: Specialization<'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
.as_ref() .as_ref()
.map(|ty| ty.apply_specialization(db, specialization)), .map(|ty| ty.apply_type_mapping(db, type_mapping)),
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_specialization(db, specialization)), .map(|ty| ty.apply_type_mapping(db, type_mapping)),
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_specialization(db, specialization)), .map(|ty| ty.apply_type_mapping(db, type_mapping)),
name: name.clone(), name: name.clone(),
}, },
Self::Variadic { .. } | Self::KeywordVariadic { .. } => self.clone(), Self::Variadic { .. } | Self::KeywordVariadic { .. } => self.clone(),