Skip to content

Commit

Permalink
Give TypeVarTupleType a fallback (#14231)
Browse files Browse the repository at this point in the history
We want to eventually decomission the use of TypeList for
constraints/expanded values of TypeVarTupleTypes. In order to do that we
will need a reference to the tuple fallback. All the places we currently
construct these TypeLists for the use in variadic generics have access
to a TypeVarTupleType, so putting the tuple fallback into the typevar
type is a reasonable way to make that information accessible where we
need it.

In order to get it into the type, we first put it into the expr by
making a symbol table lookup in semanal.
  • Loading branch information
jhance committed Dec 1, 2022
1 parent 3c71548 commit 8da17d7
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 18 deletions.
2 changes: 1 addition & 1 deletion mypy/copytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def visit_parameters(self, t: Parameters) -> ProperType:
return self.copy_common(t, dup)

def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType:
dup = TypeVarTupleType(t.name, t.fullname, t.id, t.upper_bound)
dup = TypeVarTupleType(t.name, t.fullname, t.id, t.upper_bound, t.tuple_fallback)
return self.copy_common(t, dup)

def visit_unpack_type(self, t: UnpackType) -> ProperType:
Expand Down
17 changes: 16 additions & 1 deletion mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2526,10 +2526,23 @@ def deserialize(cls, data: JsonDict) -> ParamSpecExpr:
class TypeVarTupleExpr(TypeVarLikeExpr):
"""Type variable tuple expression TypeVarTuple(...)."""

__slots__ = ()
__slots__ = "tuple_fallback"

tuple_fallback: mypy.types.Instance

__match_args__ = ("name", "upper_bound")

def __init__(
self,
name: str,
fullname: str,
upper_bound: mypy.types.Type,
tuple_fallback: mypy.types.Instance,
variance: int = INVARIANT,
) -> None:
super().__init__(name, fullname, upper_bound, variance)
self.tuple_fallback = tuple_fallback

def accept(self, visitor: ExpressionVisitor[T]) -> T:
return visitor.visit_type_var_tuple_expr(self)

Expand All @@ -2539,6 +2552,7 @@ def serialize(self) -> JsonDict:
"name": self._name,
"fullname": self._fullname,
"upper_bound": self.upper_bound.serialize(),
"tuple_fallback": self.tuple_fallback.serialize(),
"variance": self.variance,
}

Expand All @@ -2549,6 +2563,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleExpr:
data["name"],
data["fullname"],
mypy.types.deserialize_type(data["upper_bound"]),
mypy.types.Instance.deserialize(data["tuple_fallback"]),
data["variance"],
)

Expand Down
3 changes: 2 additions & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4119,8 +4119,9 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:

# PEP 646 does not specify the behavior of variance, constraints, or bounds.
if not call.analyzed:
tuple_fallback = self.named_type("builtins.tuple", [self.object_type()])
typevartuple_var = TypeVarTupleExpr(
name, self.qualified_name(name), self.object_type(), INVARIANT
name, self.qualified_name(name), self.object_type(), tuple_fallback, INVARIANT
)
typevartuple_var.line = call.line
call.analyzed = typevartuple_var
Expand Down
25 changes: 13 additions & 12 deletions mypy/test/typefixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ def make_type_var(
) -> TypeVarType:
return TypeVarType(name, name, id, values, upper_bound, variance)

def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleType:
return TypeVarTupleType(name, name, id, upper_bound)

self.t = make_type_var("T", 1, [], self.o, variance) # T`1 (type variable)
self.tf = make_type_var("T", -1, [], self.o, variance) # T`-1 (type variable)
self.tf2 = make_type_var("T", -2, [], self.o, variance) # T`-2 (type variable)
Expand All @@ -68,10 +65,6 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy
self.sf1 = make_type_var("S", -1, [], self.o, variance) # S`-1 (type variable)
self.u = make_type_var("U", 3, [], self.o, variance) # U`3 (type variable)

self.ts = make_type_var_tuple("Ts", 1, self.o) # Ts`1 (type var tuple)
self.ss = make_type_var_tuple("Ss", 2, self.o) # Ss`2 (type var tuple)
self.us = make_type_var_tuple("Us", 3, self.o) # Us`3 (type var tuple)

# Simple types
self.anyt = AnyType(TypeOfAny.special_form)
self.nonet = NoneType()
Expand Down Expand Up @@ -133,10 +126,6 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy
bases=[Instance(self.gi, [self.s1])],
)

self.gvi = self.make_type_info("GV", mro=[self.oi], typevars=["Ts"], typevar_tuple_index=0)
self.gv2i = self.make_type_info(
"GV2", mro=[self.oi], typevars=["T", "Ts", "S"], typevar_tuple_index=1
)
# list[T]
self.std_listi = self.make_type_info(
"builtins.list", mro=[self.oi], typevars=["T"], variances=[variance]
Expand Down Expand Up @@ -218,6 +207,18 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy
self._add_bool_dunder(self.bool_type_info)
self._add_bool_dunder(self.ai)

def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleType:
return TypeVarTupleType(name, name, id, upper_bound, self.std_tuple)

self.ts = make_type_var_tuple("Ts", 1, self.o) # Ts`1 (type var tuple)
self.ss = make_type_var_tuple("Ss", 2, self.o) # Ss`2 (type var tuple)
self.us = make_type_var_tuple("Us", 3, self.o) # Us`3 (type var tuple)

self.gvi = self.make_type_info("GV", mro=[self.oi], typevars=["Ts"], typevar_tuple_index=0)
self.gv2i = self.make_type_info(
"GV2", mro=[self.oi], typevars=["T", "Ts", "S"], typevar_tuple_index=1
)

def _add_bool_dunder(self, type_info: TypeInfo) -> None:
signature = CallableType([], [], [], Instance(self.bool_type_info, []), self.function)
bool_func = FuncDef("__bool__", [], Block([]))
Expand Down Expand Up @@ -296,7 +297,7 @@ def make_type_info(
v: list[TypeVarLikeType] = []
for id, n in enumerate(typevars, 1):
if typevar_tuple_index is not None and id - 1 == typevar_tuple_index:
v.append(TypeVarTupleType(n, n, id, self.o))
v.append(TypeVarTupleType(n, n, id, self.o, self.std_tuple))
else:
if variances:
variance = variances[id - 1]
Expand Down
6 changes: 5 additions & 1 deletion mypy/treetransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,11 @@ def visit_paramspec_expr(self, node: ParamSpecExpr) -> ParamSpecExpr:

def visit_type_var_tuple_expr(self, node: TypeVarTupleExpr) -> TypeVarTupleExpr:
return TypeVarTupleExpr(
node.name, node.fullname, self.type(node.upper_bound), variance=node.variance
node.name,
node.fullname,
self.type(node.upper_bound),
node.tuple_fallback,
variance=node.variance,
)

def visit_type_alias_expr(self, node: TypeAliasExpr) -> TypeAliasExpr:
Expand Down
1 change: 1 addition & 0 deletions mypy/tvar_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType:
tvar_expr.fullname,
i,
upper_bound=tvar_expr.upper_bound,
tuple_fallback=tvar_expr.tuple_fallback,
line=tvar_expr.line,
column=tvar_expr.column,
)
Expand Down
2 changes: 2 additions & 0 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,14 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
self.fail(
f'Type variable "{t.name}" used with arguments', t, code=codes.VALID_TYPE
)

# Change the line number
return TypeVarTupleType(
tvar_def.name,
tvar_def.fullname,
tvar_def.id,
tvar_def.upper_bound,
sym.node.tuple_fallback,
line=t.line,
column=t.column,
)
Expand Down
22 changes: 21 additions & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,20 @@ class TypeVarTupleType(TypeVarLikeType):
See PEP646 for more information.
"""

def __init__(
self,
name: str,
fullname: str,
id: TypeVarId | int,
upper_bound: Type,
tuple_fallback: Instance,
*,
line: int = -1,
column: int = -1,
) -> None:
super().__init__(name, fullname, id, upper_bound, line=line, column=column)
self.tuple_fallback = tuple_fallback

def serialize(self) -> JsonDict:
assert not self.id.is_meta_var()
return {
Expand All @@ -728,13 +742,18 @@ def serialize(self) -> JsonDict:
"fullname": self.fullname,
"id": self.id.raw_id,
"upper_bound": self.upper_bound.serialize(),
"tuple_fallback": self.tuple_fallback.serialize(),
}

@classmethod
def deserialize(cls, data: JsonDict) -> TypeVarTupleType:
assert data[".class"] == "TypeVarTupleType"
return TypeVarTupleType(
data["name"], data["fullname"], data["id"], deserialize_type(data["upper_bound"])
data["name"],
data["fullname"],
data["id"],
deserialize_type(data["upper_bound"]),
Instance.deserialize(data["tuple_fallback"]),
)

def accept(self, visitor: TypeVisitor[T]) -> T:
Expand All @@ -759,6 +778,7 @@ def copy_modified(self, id: Bogus[TypeVarId | int] = _dummy) -> TypeVarTupleType
self.fullname,
self.id if id is _dummy else id,
self.upper_bound,
self.tuple_fallback,
line=self.line,
column=self.column,
)
Expand Down
10 changes: 9 additions & 1 deletion mypy/typevars.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,15 @@ def fill_typevars(typ: TypeInfo) -> Instance | TupleType:
)
elif isinstance(tv, TypeVarTupleType):
tv = UnpackType(
TypeVarTupleType(tv.name, tv.fullname, tv.id, tv.upper_bound, line=-1, column=-1)
TypeVarTupleType(
tv.name,
tv.fullname,
tv.id,
tv.upper_bound,
tv.tuple_fallback,
line=-1,
column=-1,
)
)
else:
assert isinstance(tv, ParamSpecType)
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/semanal-errors.test
Original file line number Diff line number Diff line change
Expand Up @@ -1474,3 +1474,4 @@ y: Unpack[TVariadic] # E: TypeVarTuple "TVariadic" is unbound

class Variadic(Generic[Unpack[TVariadic], Unpack[TVariadic2]]): # E: Can only use one type var tuple in a class def
pass
[builtins fixtures/tuple.pyi]
1 change: 1 addition & 0 deletions test-data/unit/semanal-types.test
Original file line number Diff line number Diff line change
Expand Up @@ -1560,3 +1560,4 @@ MypyFile:1(
AssignmentStmt:2(
NameExpr(TV* [__main__.TV])
TypeVarTupleExpr:2()))
[builtins fixtures/tuple.pyi]

0 comments on commit 8da17d7

Please sign in to comment.