Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/annotations/self.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 19 additions & 12 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7549,10 +7549,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 {
Expand All @@ -7567,7 +7566,7 @@ impl<'db> Type<'db> {
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::PromoteLiterals(_) |
TypeMapping::BindSelf(_) |
TypeMapping::BindSelf { .. } |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::Materialize(_) |
TypeMapping::ReplaceParameterDefaults |
Expand Down Expand Up @@ -7743,7 +7742,7 @@ impl<'db> Type<'db> {
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::BindLegacyTypevars(_) |
TypeMapping::BindSelf(_) |
TypeMapping::BindSelf { .. } |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::Materialize(_) |
TypeMapping::ReplaceParameterDefaults |
Expand All @@ -7756,7 +7755,7 @@ impl<'db> Type<'db> {
TypeMapping::Specialization(_) |
TypeMapping::PartialSpecialization(_) |
TypeMapping::BindLegacyTypevars(_) |
TypeMapping::BindSelf(_) |
TypeMapping::BindSelf { .. } |
TypeMapping::ReplaceSelf { .. } |
TypeMapping::PromoteLiterals(_) |
TypeMapping::ReplaceParameterDefaults |
Expand Down Expand Up @@ -8420,7 +8419,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<BindingContext<'db>>,
},
/// 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.
Expand Down Expand Up @@ -8448,7 +8450,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)
Expand Down Expand Up @@ -8481,7 +8483,7 @@ impl<'db> TypeMapping<'_, 'db> {
TypeMapping::Specialization(_)
| TypeMapping::PartialSpecialization(_)
| TypeMapping::BindLegacyTypevars(_)
| TypeMapping::BindSelf(_)
| TypeMapping::BindSelf { .. }
| TypeMapping::ReplaceSelf { .. }
| TypeMapping::ReplaceParameterDefaults
| TypeMapping::EagerExpansion => self.clone(),
Expand Down Expand Up @@ -9836,8 +9838,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)
Expand Down
38 changes: 19 additions & 19 deletions crates/ty_python_semantic/src/types/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading