Skip to content

Commit 0bc852b

Browse files
committed
Fix joining of fixed tuples with mismatching lengths
For example: Tuple[bool, int] + Tuple[bool] becomes Tuple[int, ...] Previously Mypy simply punted and returned `object`. The handling of fixed tuple + variadic tuple will be implemented separately.
1 parent 41b40aa commit 0bc852b

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

mypy/join.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,28 @@ def visit_overloaded(self, t: Overloaded) -> ProperType:
260260
return join_types(t.fallback, s)
261261

262262
def visit_tuple_type(self, t: TupleType) -> ProperType:
263-
if isinstance(self.s, TupleType) and self.s.length() == t.length():
264-
items = [] # type: List[Type]
265-
for i in range(t.length()):
266-
items.append(self.join(t.items[i], self.s.items[i]))
267-
fallback = join_instances(mypy.typeops.tuple_fallback(self.s),
268-
mypy.typeops.tuple_fallback(t))
269-
assert isinstance(fallback, Instance)
270-
return TupleType(items, fallback)
263+
# When given two fixed-length tuples:
264+
# * If lengths match, join their subtypes item-wise:
265+
# Tuple[int, bool] + Tuple[bool, bool] becomes Tuple[int, bool]
266+
# * If lengths do not match, return a variadic tuple:
267+
# Tuple[bool, int] + Tuple[bool] becomes Tuple[int, ...]
268+
# * Fixed tuple + variadic tuple is currently not implemented.
269+
if isinstance(self.s, TupleType):
270+
if self.s.length() == t.length():
271+
items = [] # type: List[Type]
272+
for i in range(t.length()):
273+
items.append(self.join(t.items[i], self.s.items[i]))
274+
fallback = join_instances(mypy.typeops.tuple_fallback(self.s),
275+
mypy.typeops.tuple_fallback(t))
276+
assert isinstance(fallback, Instance)
277+
return TupleType(items, fallback)
278+
else:
279+
items = self.s.items + t.items
280+
# Otherwise they'd compare equal in above check.
281+
assert len(items), "Both tuple items cannot be empty"
282+
subtype = join_type_list(items)
283+
tuple_type = self.s.partial_fallback.type # FIXME how do I construct TypeInfo?
284+
return Instance(tuple_type, [subtype])
271285
else:
272286
return self.default(self.s)
273287

mypy/test/testtypes.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,23 @@ def test_tuples(self) -> None:
504504
self.fx.o)
505505
self.assert_join(self.tuple(self.fx.a),
506506
self.tuple(self.fx.a, self.fx.a),
507+
self.var_tuple(self.fx.a))
508+
self.assert_join(self.tuple(self.fx.b),
509+
self.tuple(self.fx.a, self.fx.c),
510+
self.var_tuple(self.fx.a))
511+
self.assert_join(self.tuple(),
512+
self.tuple(self.fx.a),
513+
self.var_tuple(self.fx.a))
514+
515+
def test_var_tuples(self) -> None:
516+
self.assert_join(self.tuple(self.fx.a),
517+
self.var_tuple(self.fx.a),
518+
self.fx.o)
519+
self.assert_join(self.var_tuple(self.fx.a),
520+
self.tuple(self.fx.a),
521+
self.fx.o)
522+
self.assert_join(self.var_tuple(self.fx.a),
523+
self.tuple(),
507524
self.fx.o)
508525

509526
def test_function_types(self) -> None:
@@ -760,6 +777,9 @@ def assert_simple_join(self, s: Type, t: Type, join: Type) -> None:
760777
def tuple(self, *a: Type) -> TupleType:
761778
return TupleType(list(a), self.fx.std_tuple)
762779

780+
def var_tuple(self, t: Type) -> Instance:
781+
return Instance(self.fx.std_tuplei, [t])
782+
763783
def callable(self, *a: Type) -> CallableType:
764784
"""callable(a1, ..., an, r) constructs a callable with argument types
765785
a1, ... an and return type r.

0 commit comments

Comments
 (0)