Meet 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)
| 1003 | return meet_types(t.fallback, s) |
| 1004 | |
| 1005 | def meet_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: |
| 1006 | """Meet two tuple types while handling variadic entries. |
| 1007 | |
| 1008 | This is surprisingly tricky, and we don't handle some tricky corner cases. |
| 1009 | Most of the trickiness comes from the variadic tuple items like *tuple[X, ...] |
| 1010 | since they can have arbitrary partial overlaps (while *Ts can't be split). This |
| 1011 | function is roughly a mirror of join_tuples() w.r.t. to the fact that fixed |
| 1012 | tuples are subtypes of variadic ones but not vice versa. |
| 1013 | """ |
| 1014 | s_unpack_index = find_unpack_in_list(s.items) |
| 1015 | t_unpack_index = find_unpack_in_list(t.items) |
| 1016 | if s_unpack_index is None and t_unpack_index is None: |
| 1017 | if s.length() == t.length(): |
| 1018 | items: list[Type] = [] |
| 1019 | for i in range(t.length()): |
| 1020 | items.append(self.meet(t.items[i], s.items[i])) |
| 1021 | return items |
| 1022 | return None |
| 1023 | if s_unpack_index is not None and t_unpack_index is not None: |
| 1024 | # The only simple case we can handle if both tuples are variadic |
| 1025 | # is when their structure fully matches. Other cases are tricky because |
| 1026 | # a variadic item is effectively a union of tuples of all length, thus |
| 1027 | # potentially causing overlap between a suffix in `s` and a prefix |
| 1028 | # in `t` (see how this is handled in is_subtype() for details). |
| 1029 | # TODO: handle more cases (like when both prefix/suffix are shorter in s or t). |
| 1030 | if s.length() == t.length() and s_unpack_index == t_unpack_index: |
| 1031 | unpack_index = s_unpack_index |
| 1032 | s_unpack = s.items[unpack_index] |
| 1033 | assert isinstance(s_unpack, UnpackType) |
| 1034 | s_unpacked = get_proper_type(s_unpack.type) |
| 1035 | t_unpack = t.items[unpack_index] |
| 1036 | assert isinstance(t_unpack, UnpackType) |
| 1037 | t_unpacked = get_proper_type(t_unpack.type) |
| 1038 | if not (isinstance(s_unpacked, Instance) and isinstance(t_unpacked, Instance)): |
| 1039 | return None |
| 1040 | meet = self.meet(s_unpacked, t_unpacked) |
| 1041 | if not isinstance(meet, Instance): |
| 1042 | return None |
| 1043 | m_prefix: list[Type] = [] |
| 1044 | for si, ti in zip(s.items[:unpack_index], t.items[:unpack_index]): |
| 1045 | m_prefix.append(meet_types(si, ti)) |
| 1046 | m_suffix: list[Type] = [] |
| 1047 | for si, ti in zip(s.items[unpack_index + 1 :], t.items[unpack_index + 1 :]): |
| 1048 | m_suffix.append(meet_types(si, ti)) |
| 1049 | return m_prefix + [UnpackType(meet)] + m_suffix |
| 1050 | return None |
| 1051 | if s_unpack_index is not None: |
| 1052 | variadic = s |
| 1053 | unpack_index = s_unpack_index |
| 1054 | fixed = t |
| 1055 | else: |
| 1056 | assert t_unpack_index is not None |
| 1057 | variadic = t |
| 1058 | unpack_index = t_unpack_index |
| 1059 | fixed = s |
| 1060 | # If one tuple is variadic one, and the other one is fixed, the meet will be fixed. |
| 1061 | unpack = variadic.items[unpack_index] |
| 1062 | assert isinstance(unpack, UnpackType) |
no test coverage detected