Join two tuple types while handling variadic entries. This is surprisingly tricky, and we don't handle some tricky corner cases. Most of the trickiness comes from the variadic tuple items like *tuple[X, ...] since they can have arbitrary partial overlaps (while *Ts can't be
(self, s: TupleType, t: TupleType)
| 468 | return join_types(t.fallback, s) |
| 469 | |
| 470 | def join_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: |
| 471 | """Join two tuple types while handling variadic entries. |
| 472 | |
| 473 | This is surprisingly tricky, and we don't handle some tricky corner cases. |
| 474 | Most of the trickiness comes from the variadic tuple items like *tuple[X, ...] |
| 475 | since they can have arbitrary partial overlaps (while *Ts can't be split). |
| 476 | """ |
| 477 | s_unpack_index = find_unpack_in_list(s.items) |
| 478 | t_unpack_index = find_unpack_in_list(t.items) |
| 479 | if s_unpack_index is None and t_unpack_index is None: |
| 480 | if s.length() == t.length(): |
| 481 | items: list[Type] = [] |
| 482 | for i in range(t.length()): |
| 483 | items.append(join_types(t.items[i], s.items[i])) |
| 484 | return items |
| 485 | return None |
| 486 | if s_unpack_index is not None and t_unpack_index is not None: |
| 487 | # The most complex case: both tuples have an unpack item. |
| 488 | s_unpack = s.items[s_unpack_index] |
| 489 | assert isinstance(s_unpack, UnpackType) |
| 490 | s_unpacked = get_proper_type(s_unpack.type) |
| 491 | t_unpack = t.items[t_unpack_index] |
| 492 | assert isinstance(t_unpack, UnpackType) |
| 493 | t_unpacked = get_proper_type(t_unpack.type) |
| 494 | if s.length() == t.length() and s_unpack_index == t_unpack_index: |
| 495 | # We can handle a case where arity is perfectly aligned, e.g. |
| 496 | # join(Tuple[X1, *tuple[Y1, ...], Z1], Tuple[X2, *tuple[Y2, ...], Z2]). |
| 497 | # We can essentially perform the join elementwise. |
| 498 | prefix_len = t_unpack_index |
| 499 | suffix_len = t.length() - t_unpack_index - 1 |
| 500 | items = [] |
| 501 | for si, ti in zip(s.items[:prefix_len], t.items[:prefix_len]): |
| 502 | items.append(join_types(si, ti)) |
| 503 | joined = join_types(s_unpacked, t_unpacked) |
| 504 | if isinstance(joined, TypeVarTupleType): |
| 505 | items.append(UnpackType(joined)) |
| 506 | elif isinstance(joined, Instance) and joined.type.fullname == "builtins.tuple": |
| 507 | items.append(UnpackType(joined)) |
| 508 | else: |
| 509 | if isinstance(t_unpacked, Instance): |
| 510 | assert t_unpacked.type.fullname == "builtins.tuple" |
| 511 | tuple_instance = t_unpacked |
| 512 | else: |
| 513 | assert isinstance(t_unpacked, TypeVarTupleType) |
| 514 | tuple_instance = t_unpacked.tuple_fallback |
| 515 | items.append( |
| 516 | UnpackType( |
| 517 | tuple_instance.copy_modified( |
| 518 | args=[object_from_instance(tuple_instance)] |
| 519 | ) |
| 520 | ) |
| 521 | ) |
| 522 | if suffix_len: |
| 523 | for si, ti in zip(s.items[-suffix_len:], t.items[-suffix_len:]): |
| 524 | items.append(join_types(si, ti)) |
| 525 | return items |
| 526 | if s.length() == 1 or t.length() == 1: |
| 527 | # Another case we can handle is when one of tuple is purely variadic |
no test coverage detected