Skip to content

Commit 331329c

Browse files
authored
Fix some daemon crashes involving classes becoming generic (#8157)
Fixes #3279. Also fixes another related crash.
1 parent 3db05b2 commit 331329c

File tree

4 files changed

+111
-14
lines changed

4 files changed

+111
-14
lines changed

mypy/constraints.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -347,29 +347,33 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
347347
template.type.has_base(instance.type.fullname)):
348348
mapped = map_instance_to_supertype(template, instance.type)
349349
tvars = mapped.type.defn.type_vars
350-
for i in range(len(instance.args)):
350+
# N.B: We use zip instead of indexing because the lengths might have
351+
# mismatches during daemon reprocessing.
352+
for tvar, mapped_arg, instance_arg in zip(tvars, mapped.args, instance.args):
351353
# The constraints for generic type parameters depend on variance.
352354
# Include constraints from both directions if invariant.
353-
if tvars[i].variance != CONTRAVARIANT:
355+
if tvar.variance != CONTRAVARIANT:
354356
res.extend(infer_constraints(
355-
mapped.args[i], instance.args[i], self.direction))
356-
if tvars[i].variance != COVARIANT:
357+
mapped_arg, instance_arg, self.direction))
358+
if tvar.variance != COVARIANT:
357359
res.extend(infer_constraints(
358-
mapped.args[i], instance.args[i], neg_op(self.direction)))
360+
mapped_arg, instance_arg, neg_op(self.direction)))
359361
return res
360362
elif (self.direction == SUPERTYPE_OF and
361363
instance.type.has_base(template.type.fullname)):
362364
mapped = map_instance_to_supertype(instance, template.type)
363365
tvars = template.type.defn.type_vars
364-
for j in range(len(template.args)):
366+
# N.B: We use zip instead of indexing because the lengths might have
367+
# mismatches during daemon reprocessing.
368+
for tvar, mapped_arg, template_arg in zip(tvars, mapped.args, template.args):
365369
# The constraints for generic type parameters depend on variance.
366370
# Include constraints from both directions if invariant.
367-
if tvars[j].variance != CONTRAVARIANT:
371+
if tvar.variance != CONTRAVARIANT:
368372
res.extend(infer_constraints(
369-
template.args[j], mapped.args[j], self.direction))
370-
if tvars[j].variance != COVARIANT:
373+
template_arg, mapped_arg, self.direction))
374+
if tvar.variance != COVARIANT:
371375
res.extend(infer_constraints(
372-
template.args[j], mapped.args[j], neg_op(self.direction)))
376+
template_arg, mapped_arg, neg_op(self.direction)))
373377
return res
374378
if (template.type.is_protocol and self.direction == SUPERTYPE_OF and
375379
# We avoid infinite recursion for structural subtypes by checking

mypy/join.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,10 @@ def join_instances(t: Instance, s: Instance) -> ProperType:
345345
if is_subtype(t, s) or is_subtype(s, t):
346346
# Compatible; combine type arguments.
347347
args = [] # type: List[Type]
348-
for i in range(len(t.args)):
349-
args.append(join_types(t.args[i], s.args[i]))
348+
# N.B: We use zip instead of indexing because the lengths might have
349+
# mismatches during daemon reprocessing.
350+
for ta, sa in zip(t.args, s.args):
351+
args.append(join_types(ta, sa))
350352
return Instance(t.type, args)
351353
else:
352354
# Incompatible; return trivial result object.

mypy/meet.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,10 @@ def visit_instance(self, t: Instance) -> ProperType:
491491
# Combine type arguments. We could have used join below
492492
# equivalently.
493493
args = [] # type: List[Type]
494-
for i in range(len(t.args)):
495-
args.append(self.meet(t.args[i], si.args[i]))
494+
# N.B: We use zip instead of indexing because the lengths might have
495+
# mismatches during daemon reprocessing.
496+
for ta, sia in zip(t.args, si.args):
497+
args.append(self.meet(ta, sia))
496498
return Instance(t.type, args)
497499
else:
498500
if state.strict_optional:

test-data/unit/fine-grained.test

+89
Original file line numberDiff line numberDiff line change
@@ -9288,3 +9288,92 @@ class B:
92889288
self.x = 0
92899289
[out]
92909290
==
9291+
9292+
[case testGenericChange1]
9293+
import a
9294+
[file a.py]
9295+
import b
9296+
def f() -> b.C: pass
9297+
[file b.py]
9298+
import a
9299+
class C: pass
9300+
[file b.py.2]
9301+
from typing import TypeVar, Generic, List
9302+
import a
9303+
9304+
T = TypeVar('T')
9305+
class C(Generic[T]): pass
9306+
9307+
reveal_type(a.f)
9308+
c: C[int]
9309+
l = a.f() if True else c
9310+
d = a.f()
9311+
d = c
9312+
c = d
9313+
9314+
x: List[C] = [a.f(), a.f()]
9315+
9316+
[out]
9317+
==
9318+
b.py:7: note: Revealed type is 'def () -> b.C[Any]'
9319+
[builtins fixtures/list.pyi]
9320+
9321+
[case testGenericChange2]
9322+
import a
9323+
[file a.py]
9324+
import b
9325+
def f() -> b.C[int]: pass
9326+
[file b.py]
9327+
from typing import TypeVar, Generic
9328+
import a
9329+
T = TypeVar('T')
9330+
class C(Generic[T]): pass
9331+
[file b.py.2]
9332+
from typing import List
9333+
import a
9334+
9335+
class C(): pass
9336+
9337+
c: C
9338+
l = a.f() if True else c
9339+
d = a.f()
9340+
d = c
9341+
c = d
9342+
9343+
x: List[C] = [a.f(), a.f()]
9344+
9345+
[builtins fixtures/list.pyi]
9346+
[out]
9347+
==
9348+
a.py:2: error: "C" expects no type arguments, but 1 given
9349+
9350+
[case testGenericChange3]
9351+
import a
9352+
[file a.py]
9353+
import b
9354+
def f() -> b.C[int]: pass
9355+
[file b.py]
9356+
from typing import TypeVar, Generic
9357+
import a
9358+
T = TypeVar('T')
9359+
class C(Generic[T]): pass
9360+
[file b.py.2]
9361+
from typing import TypeVar, Generic, List
9362+
import a
9363+
9364+
T = TypeVar('T')
9365+
S = TypeVar('S')
9366+
class C(Generic[S, T]): pass
9367+
9368+
c: C[int, str]
9369+
l = a.f() if True else c
9370+
d = a.f()
9371+
d = c
9372+
c = d
9373+
9374+
x: List[C] = [a.f(), a.f()]
9375+
9376+
[out]
9377+
==
9378+
a.py:2: error: "C" expects 2 type arguments, but 1 given
9379+
[builtins fixtures/list.pyi]

0 commit comments

Comments
 (0)