Skip to content

Commit a2396cd

Browse files
committed
[ty] fix implicit Self on generic class with typevar default
1 parent 7a347c4 commit a2396cd

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

crates/ty_python_semantic/resources/mdtest/annotations/self.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,28 @@ reveal_type(Container("a")) # revealed: Container[str]
301301
reveal_type(Container(b"a")) # revealed: Container[bytes]
302302
```
303303

304+
## Implicit self for classes with a default value for their generic parameter
305+
306+
```py
307+
from typing import Self, TypeVar, Generic
308+
309+
class Container[T = bytes]:
310+
def method(self) -> Self:
311+
return self
312+
313+
def _(c: Container[str]):
314+
reveal_type(c.method()) # revealed: Container[str]
315+
316+
T = TypeVar("T", default=bytes)
317+
318+
class LegacyContainer(Generic[T]):
319+
def method(self) -> Self:
320+
return self
321+
322+
def _(c: LegacyContainer[str]):
323+
reveal_type(c.method()) # revealed: LegacyContainer[str]
324+
```
325+
304326
## Invalid Usage
305327

306328
`Self` cannot be used in the signature of a function or variable.

crates/ty_python_semantic/src/types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7735,6 +7735,12 @@ pub enum BindingContext<'db> {
77357735
Synthetic,
77367736
}
77377737

7738+
impl<'db> From<Definition<'db>> for BindingContext<'db> {
7739+
fn from(definition: Definition<'db>) -> Self {
7740+
BindingContext::Definition(definition)
7741+
}
7742+
}
7743+
77387744
impl<'db> BindingContext<'db> {
77397745
fn name(self, db: &'db dyn Db) -> Option<String> {
77407746
match self {

crates/ty_python_semantic/src/types/signatures.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ use crate::types::function::FunctionType;
2626
use crate::types::generics::{GenericContext, typing_self, walk_generic_context};
2727
use crate::types::infer::nearest_enclosing_class;
2828
use crate::types::{
29-
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral,
30-
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass,
31-
MaterializationKind, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable,
32-
todo_type,
29+
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
30+
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
31+
TypeMapping, TypeRelation, VarianceInferable, todo_type,
3332
};
3433
use crate::{Db, FxOrderSet};
3534
use ruff_python_ast::{self as ast, name::Name};
@@ -415,9 +414,7 @@ impl<'db> Signature<'db> {
415414
let plain_return_ty = definition_expression_type(db, definition, returns.as_ref())
416415
.apply_type_mapping(
417416
db,
418-
&TypeMapping::MarkTypeVarsInferable(Some(BindingContext::Definition(
419-
definition,
420-
))),
417+
&TypeMapping::MarkTypeVarsInferable(Some(definition.into())),
421418
);
422419
if function_node.is_async && !is_generator {
423420
KnownClass::CoroutineType
@@ -1256,8 +1253,18 @@ impl<'db> Parameters<'db> {
12561253
let class = nearest_enclosing_class(db, index, scope_id).unwrap();
12571254

12581255
Some(
1259-
typing_self(db, scope_id, typevar_binding_context, class, &Type::TypeVar)
1260-
.expect("We should always find the surrounding class for an implicit self: Self annotation"),
1256+
// It looks like unnecessary work here that we create the implicit Self
1257+
// annotation using non-inferable typevars and then immediately apply
1258+
// `MarkTypeVarsInferable` to it. However, this is currently necessary to
1259+
// ensure that implicit-Self and explicit Self annotations are both treated
1260+
// the same. Marking type vars inferable will cause reification of lazy
1261+
// typevar defaults/bounds/constraints; this needs to happen for both
1262+
// implicit and explicit Self so they remain the "same" typevar.
1263+
typing_self(db, scope_id, typevar_binding_context, class, &Type::NonInferableTypeVar)
1264+
.expect("We should always find the surrounding class for an implicit self: Self annotation").apply_type_mapping(
1265+
db,
1266+
&TypeMapping::MarkTypeVarsInferable(typevar_binding_context.map(Into::into)),
1267+
)
12611268
)
12621269
} else {
12631270
// For methods of non-generic classes that are not otherwise generic (e.g. return `Self` or
@@ -1680,9 +1687,7 @@ impl<'db> Parameter<'db> {
16801687
annotated_type: parameter.annotation().map(|annotation| {
16811688
definition_expression_type(db, definition, annotation).apply_type_mapping(
16821689
db,
1683-
&TypeMapping::MarkTypeVarsInferable(Some(BindingContext::Definition(
1684-
definition,
1685-
))),
1690+
&TypeMapping::MarkTypeVarsInferable(Some(definition.into())),
16861691
)
16871692
}),
16881693
kind,

0 commit comments

Comments
 (0)