Skip to content

Commit

Permalink
Fix some daemon crashes involving classes becoming generic (python#8157)
Browse files Browse the repository at this point in the history
Fixes python#3279. Also fixes another related crash.
  • Loading branch information
msullivan authored Dec 18, 2019
1 parent 3db05b2 commit 331329c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 14 deletions.
24 changes: 14 additions & 10 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions mypy/meet.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
89 changes: 89 additions & 0 deletions test-data/unit/fine-grained.test
Original file line number Diff line number Diff line change
Expand Up @@ -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]

0 comments on commit 331329c

Please sign in to comment.