diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/self.md b/crates/ty_python_semantic/resources/mdtest/annotations/self.md index 4be59a5a63..57690b3dc3 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/self.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/self.md @@ -232,6 +232,32 @@ class C: reveal_type(not_a_method) # revealed: def not_a_method(self) -> Unknown ``` +## Different occurrences of `Self` represent different types + +Here, both `Foo.foo` and `Bar.bar` use `Self`. When accessing a bound method, we replace any +occurrences of `Self` with the bound `self` type. In this example, when we access `x.foo`, we only +want to substitute the occurrences of `Self` in `Foo.foo` — that is, occurrences of `Self@foo`. The +fact that `x` is an instance of `Foo[Self@bar]` (a completely different `Self` type) should not +affect that subtitution. If we blindly substitute all occurrences of `Self`, we would get +`Foo[Self@bar]` as the return type of the bound method. + +```py +from typing import Self + +class Foo[T]: + def foo(self: Self) -> T: + raise NotImplementedError + +class Bar: + def bar(self: Self, x: Foo[Self]): + # revealed: bound method Foo[Self@bar].foo() -> Self@bar + reveal_type(x.foo) + +def f[U: Bar](x: Foo[U]): + # revealed: bound method Foo[U@f].foo() -> U@f + reveal_type(x.foo) +``` + ## typing_extensions ```toml diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 494f4efa83..7c65a4715e 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7550,10 +7550,9 @@ impl<'db> Type<'db> { // If we are binding `typing.Self`, and this type is what we are binding `Self` to, return // early. This is not just an optimization, it also prevents us from infinitely expanding // the type, if it's something that can contain a `Self` reference. - if let TypeMapping::BindSelf(self_type) = type_mapping - && self == *self_type - { - return self; + match type_mapping { + TypeMapping::BindSelf { self_type, .. } if self == *self_type => return self, + _ => {} } match self { @@ -7568,7 +7567,7 @@ impl<'db> Type<'db> { TypeMapping::Specialization(_) | TypeMapping::PartialSpecialization(_) | TypeMapping::PromoteLiterals(_) | - TypeMapping::BindSelf(_) | + TypeMapping::BindSelf { .. } | TypeMapping::ReplaceSelf { .. } | TypeMapping::Materialize(_) | TypeMapping::ReplaceParameterDefaults | @@ -7744,7 +7743,7 @@ impl<'db> Type<'db> { TypeMapping::Specialization(_) | TypeMapping::PartialSpecialization(_) | TypeMapping::BindLegacyTypevars(_) | - TypeMapping::BindSelf(_) | + TypeMapping::BindSelf { .. } | TypeMapping::ReplaceSelf { .. } | TypeMapping::Materialize(_) | TypeMapping::ReplaceParameterDefaults | @@ -7757,7 +7756,7 @@ impl<'db> Type<'db> { TypeMapping::Specialization(_) | TypeMapping::PartialSpecialization(_) | TypeMapping::BindLegacyTypevars(_) | - TypeMapping::BindSelf(_) | + TypeMapping::BindSelf { .. } | TypeMapping::ReplaceSelf { .. } | TypeMapping::PromoteLiterals(_) | TypeMapping::ReplaceParameterDefaults | @@ -8421,7 +8420,10 @@ pub enum TypeMapping<'a, 'db> { /// being used in. BindLegacyTypevars(BindingContext<'db>), /// Binds any `typing.Self` typevar with a particular `self` class. - BindSelf(Type<'db>), + BindSelf { + self_type: Type<'db>, + binding_context: Option>, + }, /// Replaces occurrences of `typing.Self` with a new `Self` type variable with the given upper bound. ReplaceSelf { new_upper_bound: Type<'db> }, /// Create the top or bottom materialization of a type. @@ -8449,7 +8451,7 @@ impl<'db> TypeMapping<'_, 'db> { | TypeMapping::Materialize(_) | TypeMapping::ReplaceParameterDefaults | TypeMapping::EagerExpansion => context, - TypeMapping::BindSelf(_) => GenericContext::from_typevar_instances( + TypeMapping::BindSelf { .. } => GenericContext::from_typevar_instances( db, context .variables(db) @@ -8482,7 +8484,7 @@ impl<'db> TypeMapping<'_, 'db> { TypeMapping::Specialization(_) | TypeMapping::PartialSpecialization(_) | TypeMapping::BindLegacyTypevars(_) - | TypeMapping::BindSelf(_) + | TypeMapping::BindSelf { .. } | TypeMapping::ReplaceSelf { .. } | TypeMapping::ReplaceParameterDefaults | TypeMapping::EagerExpansion => self.clone(), @@ -9837,8 +9839,13 @@ impl<'db> BoundTypeVarInstance<'db> { TypeMapping::PartialSpecialization(partial) => { partial.get(db, self).unwrap_or(Type::TypeVar(self)) } - TypeMapping::BindSelf(self_type) => { - if self.typevar(db).is_self(db) { + TypeMapping::BindSelf { + self_type, + binding_context, + } => { + if self.typevar(db).is_self(db) + && binding_context.is_none_or(|context| self.binding_context(db) == context) + { *self_type } else { Type::TypeVar(self) diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 738fcfb971..d091b8a53f 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -29,9 +29,10 @@ use crate::types::generics::{ }; use crate::types::infer::nearest_enclosing_class; use crate::types::{ - ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor, - HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, - NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, VarianceInferable, todo_type, + ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral, + FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, + KnownClass, MaterializationKind, NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, + VarianceInferable, todo_type, }; use crate::{Db, FxOrderSet}; use ruff_python_ast::{self as ast, name::Name}; @@ -667,19 +668,18 @@ impl<'db> Signature<'db> { let mut parameters = Parameters::new(db, parameters); let mut return_ty = self.return_ty; if let Some(self_type) = self_type { + let self_mapping = TypeMapping::BindSelf { + self_type, + binding_context: self.definition.map(BindingContext::Definition), + }; parameters = parameters.apply_type_mapping_impl( db, - &TypeMapping::BindSelf(self_type), + &self_mapping, TypeContext::default(), &ApplyTypeMappingVisitor::default(), ); - return_ty = return_ty.map(|ty| { - ty.apply_type_mapping( - db, - &TypeMapping::BindSelf(self_type), - TypeContext::default(), - ) - }); + return_ty = return_ty + .map(|ty| ty.apply_type_mapping(db, &self_mapping, TypeContext::default())); } Self { generic_context: self.generic_context, @@ -690,19 +690,19 @@ impl<'db> Signature<'db> { } pub(crate) fn apply_self(&self, db: &'db dyn Db, self_type: Type<'db>) -> Self { + let self_mapping = TypeMapping::BindSelf { + self_type, + binding_context: self.definition.map(BindingContext::Definition), + }; let parameters = self.parameters.apply_type_mapping_impl( db, - &TypeMapping::BindSelf(self_type), + &self_mapping, TypeContext::default(), &ApplyTypeMappingVisitor::default(), ); - let return_ty = self.return_ty.map(|ty| { - ty.apply_type_mapping( - db, - &TypeMapping::BindSelf(self_type), - TypeContext::default(), - ) - }); + let return_ty = self + .return_ty + .map(|ty| ty.apply_type_mapping(db, &self_mapping, TypeContext::default())); Self { generic_context: self.generic_context, definition: self.definition,