Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Start treating erased TypeVarTuple like erased TypeVar or erased Para…
…mSpec
  • Loading branch information
A5rocks committed Jun 27, 2024
commit 51fb484ed329de1cd10c11d8b4d906f0473fa2c6
3 changes: 2 additions & 1 deletion mypy/erasetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ def visit_tuple_type(self, t: TupleType) -> Type:

def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type:
if self.erase_id(t.id):
return t.tuple_fallback.copy_modified(args=[self.replacement])
# TODO: should t.tuple_fallback become a TupleType?
return TupleType([], t.tuple_fallback, erased_typevartuple=True)
return t

def visit_param_spec(self, t: ParamSpecType) -> Type:
Expand Down
6 changes: 4 additions & 2 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,8 @@ def are_parameters_compatible(
if are_trivial_parameters(right) and not is_proper_subtype:
return True
trivial_suffix = is_trivial_suffix(right) and not is_proper_subtype
# erased typevartuples, like erased paramspecs or erased typevars are trivial
trivial_varargs = right_star and isinstance(right_star.typ, UnpackType) and isinstance(right_star.typ.type, TupleType) and right_star.typ.type.erased_typevartuple

if (
right.arg_kinds == [ARG_STAR]
Expand Down Expand Up @@ -1644,7 +1646,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N
if right_arg is None:
return False
if left_arg is None:
return not allow_partial_overlap and not trivial_suffix
return not allow_partial_overlap and not trivial_suffix and not trivial_varargs
return not is_compat(right_arg.typ, left_arg.typ)

if _incompatible(left_star, right_star) or _incompatible(left_star2, right_star2):
Expand Down Expand Up @@ -1673,7 +1675,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N
# arguments. Get all further positional args of left, and make sure
# they're more general than the corresponding member in right.
# TODO: are we handling UnpackType correctly here?
if right_star is not None and not trivial_suffix:
if right_star is not None and not trivial_suffix and not trivial_varargs:
# Synthesize an anonymous formal argument for the right
right_by_position = right.try_synthesizing_arg_from_vararg(None)
assert right_by_position is not None
Expand Down
17 changes: 13 additions & 4 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2126,6 +2126,8 @@ def with_normalized_var_args(self) -> Self:
# this should be done once in semanal_typeargs.py for user-defined types,
# and we ourselves rarely construct such type.
return self
if unpacked.erased_typevartuple:
return self
unpack_index = find_unpack_in_list(unpacked.items)
if unpack_index == 0 and len(unpacked.items) > 1:
# Already normalized.
Expand Down Expand Up @@ -2352,13 +2354,16 @@ class TupleType(ProperType):
a tuple base class. Use mypy.typeops.tuple_fallback to calculate the
precise fallback type derived from item types.
implicit: If True, derived from a tuple expression (t,....) instead of Tuple[t, ...]
erased_typevartuple: If True, this came from a (now-erased) TypeVarTuple. This
indicates that this tuple should act more like an Any.
"""

__slots__ = ("items", "partial_fallback", "implicit")
__slots__ = ("items", "partial_fallback", "implicit", "erased_typevartuple")

items: list[Type]
partial_fallback: Instance
implicit: bool
erased_typevartuple: bool

def __init__(
self,
Expand All @@ -2367,11 +2372,13 @@ def __init__(
line: int = -1,
column: int = -1,
implicit: bool = False,
erased_typevartuple: bool = False,
) -> None:
super().__init__(line, column)
self.partial_fallback = fallback
self.items = items
self.implicit = implicit
self.erased_typevartuple = erased_typevartuple

def can_be_true_default(self) -> bool:
if self.can_be_any_bool():
Expand Down Expand Up @@ -2412,19 +2419,20 @@ def accept(self, visitor: TypeVisitor[T]) -> T:
return visitor.visit_tuple_type(self)

def __hash__(self) -> int:
return hash((tuple(self.items), self.partial_fallback))
return hash((tuple(self.items), self.partial_fallback, self.erased_typevartuple))

def __eq__(self, other: object) -> bool:
if not isinstance(other, TupleType):
return NotImplemented
return self.items == other.items and self.partial_fallback == other.partial_fallback
return self.items == other.items and self.partial_fallback == other.partial_fallback and self.erased_typevartuple == other.erased_typevartuple

def serialize(self) -> JsonDict:
return {
".class": "TupleType",
"items": [t.serialize() for t in self.items],
"partial_fallback": self.partial_fallback.serialize(),
"implicit": self.implicit,
"erased_typevartuple": self.erased_typevartuple,
}

@classmethod
Expand All @@ -2434,6 +2442,7 @@ def deserialize(cls, data: JsonDict) -> TupleType:
[deserialize_type(t) for t in data["items"]],
Instance.deserialize(data["partial_fallback"]),
implicit=data["implicit"],
erased_typevartuple=data["erased_typevartuple"],
)

def copy_modified(
Expand Down Expand Up @@ -2496,7 +2505,7 @@ def slice(
return None
else:
slice_items = self.items[begin:end:stride]
return TupleType(slice_items, fallback, self.line, self.column, self.implicit)
return TupleType(slice_items, fallback, self.line, self.column, self.implicit, self.erased_typevartuple)


class TypedDictType(ProperType):
Expand Down