diff --git a/mypy/constraints.py b/mypy/constraints.py index ca254026c310..aa4ce24b65df 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -347,29 +347,33 @@ def visit_instance(self, template: Instance) -> List[Constraint]: template.type.has_base(instance.type.fullname)): mapped = map_instance_to_supertype(template, instance.type) tvars = mapped.type.defn.type_vars - for i in range(len(instance.args)): + # N.B: We use zip instead of indexing because the lengths might have + # mismatches during daemon reprocessing. + for tvar, mapped_arg, instance_arg in zip(tvars, mapped.args, instance.args): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. - if tvars[i].variance != CONTRAVARIANT: + if tvar.variance != CONTRAVARIANT: res.extend(infer_constraints( - mapped.args[i], instance.args[i], self.direction)) - if tvars[i].variance != COVARIANT: + mapped_arg, instance_arg, self.direction)) + if tvar.variance != COVARIANT: res.extend(infer_constraints( - mapped.args[i], instance.args[i], neg_op(self.direction))) + mapped_arg, instance_arg, neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname)): mapped = map_instance_to_supertype(instance, template.type) tvars = template.type.defn.type_vars - for j in range(len(template.args)): + # N.B: We use zip instead of indexing because the lengths might have + # mismatches during daemon reprocessing. + for tvar, mapped_arg, template_arg in zip(tvars, mapped.args, template.args): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. - if tvars[j].variance != CONTRAVARIANT: + if tvar.variance != CONTRAVARIANT: res.extend(infer_constraints( - template.args[j], mapped.args[j], self.direction)) - if tvars[j].variance != COVARIANT: + template_arg, mapped_arg, self.direction)) + if tvar.variance != COVARIANT: res.extend(infer_constraints( - template.args[j], mapped.args[j], neg_op(self.direction))) + template_arg, mapped_arg, neg_op(self.direction))) return res if (template.type.is_protocol and self.direction == SUPERTYPE_OF and # We avoid infinite recursion for structural subtypes by checking diff --git a/mypy/join.py b/mypy/join.py index 8989a596b70e..a2513bd36201 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -345,8 +345,10 @@ def join_instances(t: Instance, s: Instance) -> ProperType: if is_subtype(t, s) or is_subtype(s, t): # Compatible; combine type arguments. args = [] # type: List[Type] - for i in range(len(t.args)): - args.append(join_types(t.args[i], s.args[i])) + # N.B: We use zip instead of indexing because the lengths might have + # mismatches during daemon reprocessing. + for ta, sa in zip(t.args, s.args): + args.append(join_types(ta, sa)) return Instance(t.type, args) else: # Incompatible; return trivial result object. diff --git a/mypy/meet.py b/mypy/meet.py index 608faf8f25fe..548278c154da 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -491,8 +491,10 @@ def visit_instance(self, t: Instance) -> ProperType: # Combine type arguments. We could have used join below # equivalently. args = [] # type: List[Type] - for i in range(len(t.args)): - args.append(self.meet(t.args[i], si.args[i])) + # N.B: We use zip instead of indexing because the lengths might have + # mismatches during daemon reprocessing. + for ta, sia in zip(t.args, si.args): + args.append(self.meet(ta, sia)) return Instance(t.type, args) else: if state.strict_optional: diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 11e83f560eee..2dc598661fd9 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9288,3 +9288,92 @@ class B: self.x = 0 [out] == + +[case testGenericChange1] +import a +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +import a +class C: pass +[file b.py.2] +from typing import TypeVar, Generic, List +import a + +T = TypeVar('T') +class C(Generic[T]): pass + +reveal_type(a.f) +c: C[int] +l = a.f() if True else c +d = a.f() +d = c +c = d + +x: List[C] = [a.f(), a.f()] + +[out] +== +b.py:7: note: Revealed type is 'def () -> b.C[Any]' +[builtins fixtures/list.pyi] + +[case testGenericChange2] +import a +[file a.py] +import b +def f() -> b.C[int]: pass +[file b.py] +from typing import TypeVar, Generic +import a +T = TypeVar('T') +class C(Generic[T]): pass +[file b.py.2] +from typing import List +import a + +class C(): pass + +c: C +l = a.f() if True else c +d = a.f() +d = c +c = d + +x: List[C] = [a.f(), a.f()] + +[builtins fixtures/list.pyi] +[out] +== +a.py:2: error: "C" expects no type arguments, but 1 given + +[case testGenericChange3] +import a +[file a.py] +import b +def f() -> b.C[int]: pass +[file b.py] +from typing import TypeVar, Generic +import a +T = TypeVar('T') +class C(Generic[T]): pass +[file b.py.2] +from typing import TypeVar, Generic, List +import a + +T = TypeVar('T') +S = TypeVar('S') +class C(Generic[S, T]): pass + +c: C[int, str] +l = a.f() if True else c +d = a.f() +d = c +c = d + +x: List[C] = [a.f(), a.f()] + +[out] +== +a.py:2: error: "C" expects 2 type arguments, but 1 given +[builtins fixtures/list.pyi]