From ce8951ecab15552ea63b5a0c7688944284ea0906 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 4 Dec 2019 16:43:11 +0000 Subject: [PATCH] Always freeze type variables that were previously freshen (#8075) Fixes https://github.com/python/mypy/issues/8072 The fix is straightforward, just copy the logic from the working case (see last test) to two other code paths. Note that only two first cases I added crash on master, but I also add the last one for completeness because I didn't find a similar existing test case. --- mypy/checkmember.py | 4 +- test-data/unit/check-serialize.test | 58 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index e63dd73335fdf..c0f6ce2081f24 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -569,9 +569,10 @@ def analyze_var(name: str, dispatched_type = meet.meet_types(mx.original_type, itype) signature = freshen_function_type_vars(functype) signature = check_self_arg(signature, dispatched_type, var.is_classmethod, - mx.context, name, mx.msg) + mx.context, name, mx.msg) signature = bind_self(signature, mx.self_type, var.is_classmethod) expanded_signature = get_proper_type(expand_type_by_instance(signature, itype)) + freeze_type_vars(expanded_signature) if var.is_property: # A property cannot have an overloaded type => the cast is fine. assert isinstance(expanded_signature, CallableType) @@ -854,6 +855,7 @@ class B(A[str]): pass t = bind_self(t, original_type, is_classmethod=True) assert isuper is not None t = cast(CallableType, expand_type_by_instance(t, isuper)) + freeze_type_vars(t) return t.copy_modified(variables=tvars + t.variables) elif isinstance(t, Overloaded): return Overloaded([cast(CallableType, add_class_tvars(item, isuper, diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index c7b1c92541c57..6f67d222fcf55 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -1336,3 +1336,61 @@ import b def foo() -> None: class Foo: class Bar: pass + +[case testSerializeGenericClassMethod] +import a +[file a.py] +import b +from typing import TypeVar + +T = TypeVar('T') + +class C: + @classmethod + def f(cls, x: T) -> T: ... + +x = C.f +[file b.py] +x = 1 +[file b.py.2] +x = 'yes' +[builtins fixtures/classmethod.pyi] + +[case testSerializeGenericAbstractMethod] +import a +[file a.py] +import b +from typing import TypeVar +from abc import abstractmethod + +T = TypeVar('T') + +class C: + @abstractmethod + def f(self, x: T) -> T: ... + +c: C +x = c.f +[file b.py] +x = 1 +[file b.py.2] +x = 'yes' + +[case testSerializeGenericNormalMethod] +import a +[file a.py] +import b +from typing import TypeVar +from abc import abstractmethod + +T = TypeVar('T') + +class C: + def f(self, x: T) -> T: ... + +c: C +x = c.f +[file b.py] +x = 1 +[file b.py.2] +x = 'yes'