diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 203c71b4e8243..7933283b24d6a 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -222,6 +222,10 @@ def visit_instance(self, t: Instance) -> Type: return args def visit_type_var(self, t: TypeVarType) -> Type: + # Normally upper bounds can't contain other type variables, the only exception is + # special type variable Self`0 <: C[T, S], where C is the class where Self is used. + if t.id.raw_id == 0: + t = t.copy_modified(upper_bound=t.upper_bound.accept(self)) repl = self.variables.get(t.id, t) if isinstance(repl, ProperType) and isinstance(repl, Instance): # TODO: do we really need to do this? diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index dd177e143aaa0..2d45d28764a05 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1785,3 +1785,23 @@ class C(B, Generic[T]): inst = super().copy() reveal_type(inst) # N: Revealed type is "Self`0" return inst + +[case testTypingSelfWithValuesExpansion] +from typing import Self, Generic, TypeVar + +class A: pass +class B: pass +T = TypeVar("T", A, B) + +class C(Generic[T]): + val: T + def foo(self, x: T) -> None: ... + def bar(self, x: T) -> Self: + reveal_type(self.foo) # N: Revealed type is "def (x: __main__.A)" \ + # N: Revealed type is "def (x: __main__.B)" + self.foo(x) + return self + def baz(self: Self, x: T) -> None: + reveal_type(self.val) # N: Revealed type is "__main__.A" \ + # N: Revealed type is "__main__.B" + self.val = x