Skip to content

Commit b29ad88

Browse files
committed
Implement more cases for typevartuple subtyping.
1 parent 15e4de5 commit b29ad88

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

mypy/subtypes.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -319,19 +319,36 @@ def check_mixed(
319319
return False
320320
if isinstance(unpacked_type, AnyType):
321321
return True
322-
assert False
322+
if isinstance(unpacked_type, TupleType):
323+
if len(unpacked_type.items) != len(compare_to):
324+
return False
325+
for t1, t2 in zip(unpacked_type.items, compare_to):
326+
if not is_equivalent(t1, t2):
327+
return False
328+
return True
329+
return False
323330

324331
# Case 1: Both are unpacks, in this case we check what is being
325332
# unpacked is the same.
326333
if left_unpacked is not None and right_unpacked is not None:
327334
if not is_equivalent(left_unpacked, right_unpacked):
328335
return False
329336

330-
# Case 2: Only one of the types is an unpack.
337+
# Case 2: Only one of the types is an unpack. The equivalence
338+
# case is mostly the same but we check some additional
339+
# things when unpacking on the right.
331340
elif left_unpacked is not None and right_unpacked is None:
332341
if not check_mixed(left_unpacked, right_middle):
333342
return False
334343
elif left_unpacked is None and right_unpacked is not None:
344+
if (
345+
isinstance(right_unpacked, Instance)
346+
and right_unpacked.type.fullname == "builtins.tuple"
347+
):
348+
return all(
349+
is_equivalent(l, right_unpacked.args[0])
350+
for l in left_middle
351+
)
335352
if not check_mixed(right_unpacked, left_middle):
336353
return False
337354

mypy/test/testsubtypes.py

+84-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from mypy.nodes import CONTRAVARIANT, INVARIANT, COVARIANT
33
from mypy.subtypes import is_subtype
44
from mypy.test.typefixture import TypeFixture, InterfaceTypeFixture
5-
from mypy.types import Type, Instance, UnpackType
5+
from mypy.types import Type, Instance, UnpackType, TupleType
66

77

88
class SubtypingSuite(Suite):
@@ -217,6 +217,89 @@ def test_type_var_tuple(self) -> None:
217217
Instance(self.fx.gvi, [self.fx.anyt]),
218218
)
219219

220+
def test_type_var_tuple_with_prefix_suffix(self) -> None:
221+
self.assert_subtype(
222+
Instance(self.fx.gvi, [self.fx.a, UnpackType(self.fx.ss)]),
223+
Instance(self.fx.gvi, [self.fx.a, UnpackType(self.fx.ss)]),
224+
)
225+
self.assert_subtype(
226+
Instance(self.fx.gvi, [self.fx.a, self.fx.b, UnpackType(self.fx.ss)]),
227+
Instance(self.fx.gvi, [self.fx.a, self.fx.b, UnpackType(self.fx.ss)]),
228+
)
229+
self.assert_not_subtype(
230+
Instance(self.fx.gvi, [self.fx.a, UnpackType(self.fx.ss)]),
231+
Instance(self.fx.gvi, [self.fx.b, UnpackType(self.fx.ss)]),
232+
)
233+
self.assert_not_subtype(
234+
Instance(self.fx.gvi, [self.fx.a, UnpackType(self.fx.ss)]),
235+
Instance(self.fx.gvi, [self.fx.a, self.fx.b, UnpackType(self.fx.ss)]),
236+
)
237+
238+
self.assert_subtype(
239+
Instance(self.fx.gvi, [UnpackType(self.fx.ss), self.fx.a]),
240+
Instance(self.fx.gvi, [UnpackType(self.fx.ss), self.fx.a]),
241+
)
242+
self.assert_not_subtype(
243+
Instance(self.fx.gvi, [UnpackType(self.fx.ss), self.fx.a]),
244+
Instance(self.fx.gvi, [UnpackType(self.fx.ss), self.fx.b]),
245+
)
246+
self.assert_not_subtype(
247+
Instance(self.fx.gvi, [UnpackType(self.fx.ss), self.fx.a]),
248+
Instance(self.fx.gvi, [UnpackType(self.fx.ss), self.fx.a, self.fx.b]),
249+
)
250+
251+
self.assert_subtype(
252+
Instance(self.fx.gvi, [self.fx.a, self.fx.b, UnpackType(self.fx.ss), self.fx.c]),
253+
Instance(self.fx.gvi, [self.fx.a, self.fx.b, UnpackType(self.fx.ss), self.fx.c]),
254+
)
255+
self.assert_not_subtype(
256+
Instance(self.fx.gvi, [self.fx.a, self.fx.b, UnpackType(self.fx.ss), self.fx.c]),
257+
Instance(self.fx.gvi, [self.fx.a, UnpackType(self.fx.ss), self.fx.b, self.fx.c]),
258+
)
259+
260+
def test_type_var_tuple_unpacked_tuple(self) -> None:
261+
self.assert_subtype(
262+
Instance(self.fx.gvi, [
263+
UnpackType(TupleType(
264+
[self.fx.a, self.fx.b], fallback=Instance(self.fx.std_tuplei, [self.fx.o]),
265+
))
266+
]),
267+
Instance(self.fx.gvi, [self.fx.a, self.fx.b]),
268+
)
269+
self.assert_subtype(
270+
Instance(self.fx.gvi, [
271+
UnpackType(TupleType(
272+
[self.fx.a, self.fx.b], fallback=Instance(self.fx.std_tuplei, [self.fx.o]),
273+
))
274+
]),
275+
Instance(self.fx.gvi, [self.fx.anyt, self.fx.anyt]),
276+
)
277+
self.assert_not_subtype(
278+
Instance(self.fx.gvi, [
279+
UnpackType(TupleType(
280+
[self.fx.a, self.fx.b], fallback=Instance(self.fx.std_tuplei, [self.fx.o]),
281+
))
282+
]),
283+
Instance(self.fx.gvi, [self.fx.a]),
284+
)
285+
self.assert_not_subtype(
286+
Instance(self.fx.gvi, [
287+
UnpackType(TupleType(
288+
[self.fx.a, self.fx.b], fallback=Instance(self.fx.std_tuplei, [self.fx.o]),
289+
))
290+
]),
291+
# Order flipped here.
292+
Instance(self.fx.gvi, [self.fx.b, self.fx.a]),
293+
)
294+
295+
def test_type_var_tuple_unpacked_variable_length_tuple(self) -> None:
296+
self.assert_strict_subtype(
297+
Instance(self.fx.gvi, [self.fx.a, self.fx.a]),
298+
Instance(self.fx.gvi, [
299+
UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])),
300+
]),
301+
)
302+
220303
# IDEA: Maybe add these test cases (they are tested pretty well in type
221304
# checker tests already):
222305
# * more interface subtyping test cases

0 commit comments

Comments
 (0)