diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/self.md b/crates/ty_python_semantic/resources/mdtest/annotations/self.md index be8b660eb9..f06862e4b0 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/self.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/self.md @@ -301,6 +301,30 @@ reveal_type(Container("a")) # revealed: Container[str] reveal_type(Container(b"a")) # revealed: Container[bytes] ``` +## Implicit self for classes with a default value for their generic parameter + +```py +from typing import Self, TypeVar, Generic + +class Container[T = bytes]: + def method(self) -> Self: + return self + +def _(c: Container[str], d: Container): + reveal_type(c.method()) # revealed: Container[str] + reveal_type(d.method()) # revealed: Container[bytes] + +T = TypeVar("T", default=bytes) + +class LegacyContainer(Generic[T]): + def method(self) -> Self: + return self + +def _(c: LegacyContainer[str], d: LegacyContainer): + reveal_type(c.method()) # revealed: LegacyContainer[str] + reveal_type(d.method()) # revealed: LegacyContainer[bytes] +``` + ## Invalid Usage `Self` cannot be used in the signature of a function or variable. diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index f9430370a9..b6de9b7f39 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7735,6 +7735,12 @@ pub enum BindingContext<'db> { Synthetic, } +impl<'db> From> for BindingContext<'db> { + fn from(definition: Definition<'db>) -> Self { + BindingContext::Definition(definition) + } +} + impl<'db> BindingContext<'db> { fn name(self, db: &'db dyn Db) -> Option { match self { diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 4601f75541..4d72f98157 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -26,10 +26,9 @@ use crate::types::function::FunctionType; use crate::types::generics::{GenericContext, typing_self, walk_generic_context}; use crate::types::infer::nearest_enclosing_class; use crate::types::{ - ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral, - FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, - MaterializationKind, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable, - todo_type, + ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor, + HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor, + TypeMapping, TypeRelation, VarianceInferable, todo_type, }; use crate::{Db, FxOrderSet}; use ruff_python_ast::{self as ast, name::Name}; @@ -415,9 +414,7 @@ impl<'db> Signature<'db> { let plain_return_ty = definition_expression_type(db, definition, returns.as_ref()) .apply_type_mapping( db, - &TypeMapping::MarkTypeVarsInferable(Some(BindingContext::Definition( - definition, - ))), + &TypeMapping::MarkTypeVarsInferable(Some(definition.into())), ); if function_node.is_async && !is_generator { KnownClass::CoroutineType @@ -1256,8 +1253,18 @@ impl<'db> Parameters<'db> { let class = nearest_enclosing_class(db, index, scope_id).unwrap(); Some( - typing_self(db, scope_id, typevar_binding_context, class, &Type::TypeVar) - .expect("We should always find the surrounding class for an implicit self: Self annotation"), + // It looks like unnecessary work here that we create the implicit Self + // annotation using non-inferable typevars and then immediately apply + // `MarkTypeVarsInferable` to it. However, this is currently necessary to + // ensure that implicit-Self and explicit Self annotations are both treated + // the same. Marking type vars inferable will cause reification of lazy + // typevar defaults/bounds/constraints; this needs to happen for both + // implicit and explicit Self so they remain the "same" typevar. + typing_self(db, scope_id, typevar_binding_context, class, &Type::NonInferableTypeVar) + .expect("We should always find the surrounding class for an implicit self: Self annotation").apply_type_mapping( + db, + &TypeMapping::MarkTypeVarsInferable(None), + ) ) } else { // For methods of non-generic classes that are not otherwise generic (e.g. return `Self` or @@ -1680,9 +1687,7 @@ impl<'db> Parameter<'db> { annotated_type: parameter.annotation().map(|annotation| { definition_expression_type(db, definition, annotation).apply_type_mapping( db, - &TypeMapping::MarkTypeVarsInferable(Some(BindingContext::Definition( - definition, - ))), + &TypeMapping::MarkTypeVarsInferable(Some(definition.into())), ) }), kind,