mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 05:25:17 +00:00
[ty] Specialize bound methods and nominal instances (#17865)
Fixes https://github.com/astral-sh/ruff/pull/17832#issuecomment-2851224968. We had a comment that we did not need to apply specializations to generic aliases, or to the bound `self` of a bound method, because they were already specialized. But they might be specialized with a type variable, which _does_ need to be specialized, in the case of a "multi-step" specialization, such as: ```py class LinkedList[T]: ... class C[U]: def method(self) -> LinkedList[U]: return LinkedList[U]() ``` --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
9a6633da0b
commit
47e3aa40b3
6 changed files with 106 additions and 13 deletions
|
@ -4962,20 +4962,16 @@ impl<'db> Type<'db> {
|
|||
Type::FunctionLiteral(function.apply_specialization(db, specialization))
|
||||
}
|
||||
|
||||
// Note that we don't need to apply the specialization to `self_instance`, since it
|
||||
// must either be a non-generic class literal (which cannot have any typevars to
|
||||
// specialize) or a generic alias (which has already been fully specialized). For a
|
||||
// generic alias, the specialization being applied here must be for some _other_
|
||||
// generic context nested within the generic alias's class literal, which the generic
|
||||
// alias's context cannot refer to. (The _method_ does need to be specialized, since it
|
||||
// might be a nested generic method, whose generic context is what is now being
|
||||
// specialized.)
|
||||
Type::BoundMethod(method) => Type::BoundMethod(BoundMethodType::new(
|
||||
db,
|
||||
method.function(db).apply_specialization(db, specialization),
|
||||
method.self_instance(db),
|
||||
method.self_instance(db).apply_specialization(db, specialization),
|
||||
)),
|
||||
|
||||
Type::NominalInstance(instance) => Type::NominalInstance(
|
||||
instance.apply_specialization(db, specialization),
|
||||
),
|
||||
|
||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(
|
||||
function.apply_specialization(db, specialization),
|
||||
|
@ -5060,9 +5056,6 @@ impl<'db> Type<'db> {
|
|||
| Type::BytesLiteral(_)
|
||||
| Type::SliceLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
// `NominalInstance` contains a ClassType, which has already been specialized if needed,
|
||||
// like above with BoundMethod's self_instance.
|
||||
| Type::NominalInstance(_)
|
||||
// Same for `ProtocolInstance`
|
||||
| Type::ProtocolInstance(_)
|
||||
| Type::KnownInstance(_) => self,
|
||||
|
|
|
@ -146,6 +146,19 @@ impl<'db> GenericAlias<'db> {
|
|||
pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
|
||||
self.origin(db).definition(db)
|
||||
}
|
||||
|
||||
pub(super) fn apply_specialization(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: Specialization<'db>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.origin(db),
|
||||
self.specialization(db)
|
||||
.apply_specialization(db, specialization),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<GenericAlias<'db>> for Type<'db> {
|
||||
|
@ -210,6 +223,19 @@ impl<'db> ClassType<'db> {
|
|||
self.is_known(db, KnownClass::Object)
|
||||
}
|
||||
|
||||
pub(super) fn apply_specialization(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: Specialization<'db>,
|
||||
) -> Self {
|
||||
match self {
|
||||
Self::NonGeneric(_) => self,
|
||||
Self::Generic(generic) => {
|
||||
Self::Generic(generic.apply_specialization(db, specialization))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over the [method resolution order] ("MRO") of the class.
|
||||
///
|
||||
/// If the MRO could not be accurately resolved, this method falls back to iterating
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use super::protocol_class::ProtocolInterface;
|
||||
use super::{ClassType, KnownClass, SubclassOfType, Type};
|
||||
use crate::symbol::{Symbol, SymbolAndQualifiers};
|
||||
use crate::types::generics::Specialization;
|
||||
use crate::Db;
|
||||
|
||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||
|
@ -111,6 +112,16 @@ impl<'db> NominalInstanceType<'db> {
|
|||
pub(super) fn to_meta_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
SubclassOfType::from(db, self.class)
|
||||
}
|
||||
|
||||
pub(super) fn apply_specialization(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: Specialization<'db>,
|
||||
) -> Self {
|
||||
Self {
|
||||
class: self.class.apply_specialization(db, specialization),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<NominalInstanceType<'db>> for Type<'db> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue