diff --git a/mypy/semanal.py b/mypy/semanal.py index fe3151ce6cd2..696854e1dbb6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1149,6 +1149,17 @@ def analyze_class(self, defn: ClassDef) -> None: self.mark_incomplete(defn.name, defn) return + # update the typevar ids such that they will not conflict with any base classes + # (yuck, there has to be a better way to do this.) + if any(isinstance(base[0], Instance) for base in base_types): + offset = max( + self.find_maximum_class_id(base[0].type) + for base in base_types if isinstance(base[0], Instance) + ) + # mutating the type vars to be what we want (and hoping nothing previously saved them) + for tvar in tvar_defs: + tvar.id.raw_id += offset + is_typeddict, info = self.typed_dict_analyzer.analyze_typeddict_classdef(defn) if is_typeddict: for decorator in defn.decorators: @@ -1183,6 +1194,16 @@ def analyze_class(self, defn: ClassDef) -> None: self.analyze_class_decorator(defn, decorator) self.analyze_class_body_common(defn) + # should this be memoized in TypeInfo? + def find_maximum_class_id(self, info: TypeInfo) -> int: + if info.bases: + return len(info.type_vars) + max( + self.find_maximum_class_id(cls.type) + for cls in info.bases + ) + else: + return len(info.type_vars) + def is_core_builtin_class(self, defn: ClassDef) -> bool: return self.cur_mod_id == 'builtins' and defn.name in CORE_BUILTIN_CLASSES diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 2fa5a0da7e90..c872329f0f79 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -1933,9 +1933,9 @@ class C(Generic[T]): class D(C[Tuple[T, S]]): ... class E(D[S, str]): ... -reveal_type(D.make_one) # N: Revealed type is "def [T, S] (x: Tuple[T`1, S`2]) -> __main__.C[Tuple[T`1, S`2]]" +reveal_type(D.make_one) # N: Revealed type is "def [T, S] (x: Tuple[T`2, S`3]) -> __main__.C[Tuple[T`2, S`3]]" reveal_type(D[int, str].make_one) # N: Revealed type is "def (x: Tuple[builtins.int*, builtins.str*]) -> __main__.C[Tuple[builtins.int*, builtins.str*]]" -reveal_type(E.make_one) # N: Revealed type is "def [S] (x: Tuple[S`1, builtins.str*]) -> __main__.C[Tuple[S`1, builtins.str*]]" +reveal_type(E.make_one) # N: Revealed type is "def [S] (x: Tuple[S`4, builtins.str*]) -> __main__.C[Tuple[S`4, builtins.str*]]" reveal_type(E[int].make_one) # N: Revealed type is "def (x: Tuple[builtins.int*, builtins.str*]) -> __main__.C[Tuple[builtins.int*, builtins.str*]]" [builtins fixtures/classmethod.pyi] @@ -2111,11 +2111,11 @@ class A(Generic[T]): class B(A[T], Generic[T, S]): def meth(self) -> None: - reveal_type(A[T].foo) # N: Revealed type is "def () -> Tuple[T`1, __main__.A[T`1]]" + reveal_type(A[T].foo) # N: Revealed type is "def () -> Tuple[T`2, __main__.A[T`2]]" @classmethod def other(cls) -> None: - reveal_type(cls.foo) # N: Revealed type is "def () -> Tuple[T`1, __main__.B[T`1, S`2]]" -reveal_type(B.foo) # N: Revealed type is "def [T, S] () -> Tuple[T`1, __main__.B[T`1, S`2]]" + reveal_type(cls.foo) # N: Revealed type is "def () -> Tuple[T`2, __main__.B[T`2, S`3]]" +reveal_type(B.foo) # N: Revealed type is "def [T, S] () -> Tuple[T`2, __main__.B[T`2, S`3]]" [builtins fixtures/classmethod.pyi] [case testGenericClassAlternativeConstructorPrecise] @@ -2504,3 +2504,24 @@ b: I[I[Any]] reveal_type([a, b]) # N: Revealed type is "builtins.list[__main__.I*[__main__.I[Any]]]" reveal_type([b, a]) # N: Revealed type is "builtins.list[__main__.I*[__main__.I[Any]]]" [builtins fixtures/list.pyi] + +[case testOverlappingTypeVarIds] +from typing import TypeVar, Generic + +class A: ... +class B: ... + +T = TypeVar("T", bound=A) +V = TypeVar("V", bound=B) +S = TypeVar("S") + +class Whatever(Generic[T]): + def something(self: S) -> S: + return self + +# the "V" here had the same id as "T" and so mypy used to think it could expand one into another. +# this test is here to make sure that doesn't happen! +class WhateverPartTwo(Whatever[A], Generic[V]): + def something(self: S) -> S: + return self + diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 772de61b5900..f0e1e31e9499 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -662,9 +662,9 @@ MypyFile:1( ClassDef:4( c TypeVars( - t`1) + t`2) BaseType( - __main__.d[t`1]) + __main__.d[t`2]) PassStmt:4())) [case testTupleType] @@ -907,7 +907,7 @@ MypyFile:1( ClassDef:5( A TypeVars( - t`1) + t`2) BaseType( __main__.B[Any]) PassStmt:5())) diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index 4f40117d18d2..260515663880 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -435,7 +435,7 @@ class B(A[C, T], Generic[T]): CallExpr(9) : None MemberExpr(9) : def (a: C) NameExpr(9) : C -NameExpr(9) : B[T`1] +NameExpr(9) : B[T`3] [case testExternalReferenceWithGenericInheritance] from typing import TypeVar, Generic