Skip to content

Commit

Permalink
Merge TypeVarDef and TypeVarType (#9951)
Browse files Browse the repository at this point in the history
In the spirit of #4814 (comment)

I'm not sure what the distinction between TypeVarDef and TypeVarType was (note that there's also TypeVarExpr, whose purpose is clear to me).
In practice, it seems it boiled down to: a) TypeVarDef has a different __repr__, that's not user facing but was used in tests, b) in two places, we used the construction of a TypeVarType from a TypeVarDef as an opportunity to change the line number.

I ran into this because:
a) I was considering whether to create a corresponding ParamSpecDef and ParamSpecType
b) I was trying to fix an issue with applytype sometimes swallowing generics, which to fix I needed TypeVarType to have access to its TypeVarDef.
I was unable to find a reason for TypeVarType to not just be the TypeVarDef and here we are.

Co-authored-by: hauntsaninja <>
  • Loading branch information
hauntsaninja authored Aug 4, 2021
1 parent 3e5b9b1 commit 2736edb
Show file tree
Hide file tree
Showing 27 changed files with 216 additions and 269 deletions.
10 changes: 5 additions & 5 deletions mypy/applytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@
from mypy.expandtype import expand_type
from mypy.types import (
Type, TypeVarId, TypeVarType, CallableType, AnyType, PartialType, get_proper_types,
TypeVarDef, TypeVarLikeDef, ProperType, ParamSpecDef
TypeVarLikeType, ProperType, ParamSpecType
)
from mypy.nodes import Context


def get_target_type(
tvar: TypeVarLikeDef,
tvar: TypeVarLikeType,
type: ProperType,
callable: CallableType,
report_incompatible_typevar_value: Callable[[CallableType, Type, str, Context], None],
context: Context,
skip_unsatisfied: bool
) -> Optional[Type]:
# TODO(shantanu): fix for ParamSpecDef
if isinstance(tvar, ParamSpecDef):
# TODO(shantanu): fix for ParamSpecType
if isinstance(tvar, ParamSpecType):
return None
assert isinstance(tvar, TypeVarDef)
assert isinstance(tvar, TypeVarType)
values = get_proper_types(tvar.values)
if values:
if isinstance(type, AnyType):
Expand Down
6 changes: 3 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from mypy.types import (
Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType,
Instance, NoneType, strip_type, TypeType, TypeOfAny,
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarDef,
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType,
is_named_instance, union_items, TypeQuery, LiteralType,
is_optional, remove_optional, TypeTranslator, StarType, get_proper_type, ProperType,
get_proper_types, is_literal_type, TypeAliasType, TypeGuardType)
Expand Down Expand Up @@ -1403,7 +1403,7 @@ def expand_typevars(self, defn: FuncItem,
tvars += defn.info.defn.type_vars or []
# TODO(shantanu): audit for paramspec
for tvar in tvars:
if isinstance(tvar, TypeVarDef) and tvar.values:
if isinstance(tvar, TypeVarType) and tvar.values:
subst.append([(tvar.id, value) for value in tvar.values])
# Make a copy of the function to check for each combination of
# value restricted type variables. (Except when running mypyc,
Expand Down Expand Up @@ -5504,7 +5504,7 @@ def detach_callable(typ: CallableType) -> CallableType:
for var in set(all_type_vars):
if var.fullname not in used_type_var_names:
continue
new_variables.append(TypeVarDef(
new_variables.append(TypeVarType(
name=var.name,
fullname=var.fullname,
id=var.id,
Expand Down
68 changes: 31 additions & 37 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
make_optional_type,
)
from mypy.types import (
Type, AnyType, CallableType, Overloaded, NoneType, TypeGuardType, TypeVarDef,
TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType,
Type, AnyType, CallableType, Overloaded, NoneType, TypeVarType, TypeGuardType,
TupleType, TypedDictType, Instance, ErasedType, UnionType,
PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, LiteralType, LiteralValue,
is_named_instance, FunctionLike, ParamSpecDef,
is_named_instance, FunctionLike, ParamSpecType,
StarType, is_optional, remove_optional, is_generic_instance, get_proper_type, ProperType,
get_proper_types, flatten_nested_unions
)
Expand Down Expand Up @@ -1904,8 +1904,8 @@ def combine_function_signatures(self, types: Sequence[Type]) -> Union[AnyType, C
# same thing.
#
# This function will make sure that all instances of that TypeVar 'T'
# refer to the same underlying TypeVarType and TypeVarDef objects to
# simplify the union-ing logic below.
# refer to the same underlying TypeVarType objects to simplify the union-ing
# logic below.
#
# (If the user did *not* mean for 'T' to be consistently bound to the
# same type in their overloads, well, their code is probably too
Expand Down Expand Up @@ -3277,16 +3277,15 @@ def check_lst_expr(self, items: List[Expression], fullname: str,
# Used for list and set expressions, as well as for tuples
# containing star expressions that don't refer to a
# Tuple. (Note: "lst" stands for list-set-tuple. :-)
tvdef = TypeVarDef('T', 'T', -1, [], self.object_type())
tv = TypeVarType(tvdef)
tv = TypeVarType('T', 'T', -1, [], self.object_type())
constructor = CallableType(
[tv],
[nodes.ARG_STAR],
[None],
self.chk.named_generic_type(fullname, [tv]),
self.named_type('builtins.function'),
name=tag,
variables=[tvdef])
variables=[tv])
out = self.check_call(constructor,
[(i.expr if isinstance(i, StarExpr) else i)
for i in items],
Expand Down Expand Up @@ -3435,10 +3434,8 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
tup.column = value.column
args.append(tup)
# Define type variables (used in constructors below).
ktdef = TypeVarDef('KT', 'KT', -1, [], self.object_type())
vtdef = TypeVarDef('VT', 'VT', -2, [], self.object_type())
kt = TypeVarType(ktdef)
vt = TypeVarType(vtdef)
kt = TypeVarType('KT', 'KT', -1, [], self.object_type())
vt = TypeVarType('VT', 'VT', -2, [], self.object_type())
rv = None
# Call dict(*args), unless it's empty and stargs is not.
if args or not stargs:
Expand All @@ -3452,7 +3449,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
self.chk.named_generic_type('builtins.dict', [kt, vt]),
self.named_type('builtins.function'),
name='<dict>',
variables=[ktdef, vtdef])
variables=[kt, vt])
rv = self.check_call(constructor, args, [nodes.ARG_POS] * len(args), e)[0]
else:
# dict(...) will be called below.
Expand All @@ -3469,7 +3466,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
self.chk.named_generic_type('builtins.dict', [kt, vt]),
self.named_type('builtins.function'),
name='<list>',
variables=[ktdef, vtdef])
variables=[kt, vt])
rv = self.check_call(constructor, [arg], [nodes.ARG_POS], arg)[0]
else:
self.check_method_call_by_name('update', rv, [arg], [nodes.ARG_POS], arg)
Expand Down Expand Up @@ -3759,16 +3756,16 @@ def check_generator_or_comprehension(self, gen: GeneratorExpr,

# Infer the type of the list comprehension by using a synthetic generic
# callable type.
tvdef = TypeVarDef('T', 'T', -1, [], self.object_type())
tv_list: List[Type] = [TypeVarType(tvdef)]
tv = TypeVarType('T', 'T', -1, [], self.object_type())
tv_list: List[Type] = [tv]
constructor = CallableType(
tv_list,
[nodes.ARG_POS],
[None],
self.chk.named_generic_type(type_name, tv_list + additional_args),
self.chk.named_type('builtins.function'),
name=id_for_messages,
variables=[tvdef])
variables=[tv])
return self.check_call(constructor,
[gen.left_expr], [nodes.ARG_POS], gen)[0]

Expand All @@ -3779,15 +3776,13 @@ def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type:

# Infer the type of the list comprehension by using a synthetic generic
# callable type.
ktdef = TypeVarDef('KT', 'KT', -1, [], self.object_type())
vtdef = TypeVarDef('VT', 'VT', -2, [], self.object_type())
kt = TypeVarType(ktdef)
vt = TypeVarType(vtdef)
ktdef = TypeVarType('KT', 'KT', -1, [], self.object_type())
vtdef = TypeVarType('VT', 'VT', -2, [], self.object_type())
constructor = CallableType(
[kt, vt],
[ktdef, vtdef],
[nodes.ARG_POS, nodes.ARG_POS],
[None, None],
self.chk.named_generic_type('builtins.dict', [kt, vt]),
self.chk.named_generic_type('builtins.dict', [ktdef, vtdef]),
self.chk.named_type('builtins.function'),
name='<dictionary-comprehension>',
variables=[ktdef, vtdef])
Expand Down Expand Up @@ -4450,7 +4445,7 @@ def all_same_types(types: List[Type]) -> bool:


def merge_typevars_in_callables_by_name(
callables: Sequence[CallableType]) -> Tuple[List[CallableType], List[TypeVarDef]]:
callables: Sequence[CallableType]) -> Tuple[List[CallableType], List[TypeVarType]]:
"""Takes all the typevars present in the callables and 'combines' the ones with the same name.
For example, suppose we have two callables with signatures "f(x: T, y: S) -> T" and
Expand All @@ -4459,34 +4454,33 @@ def merge_typevars_in_callables_by_name(
distinct ids.)
If we pass in both callables into this function, it returns a a list containing two
new callables that are identical in signature, but use the same underlying TypeVarDef
and TypeVarType objects for T and S.
new callables that are identical in signature, but use the same underlying TypeVarType
for T and S.
This is useful if we want to take the output lists and "merge" them into one callable
in some way -- for example, when unioning together overloads.
Returns both the new list of callables and a list of all distinct TypeVarDef objects used.
Returns both the new list of callables and a list of all distinct TypeVarType objects used.
"""

output: List[CallableType] = []
unique_typevars: Dict[str, TypeVarType] = {}
variables: List[TypeVarDef] = []
variables: List[TypeVarType] = []

for target in callables:
if target.is_generic():
target = freshen_function_type_vars(target)

rename = {} # Dict[TypeVarId, TypeVar]
for tvdef in target.variables:
name = tvdef.fullname
for tv in target.variables:
name = tv.fullname
if name not in unique_typevars:
# TODO(shantanu): fix for ParamSpecDef
if isinstance(tvdef, ParamSpecDef):
# TODO(shantanu): fix for ParamSpecType
if isinstance(tv, ParamSpecType):
continue
assert isinstance(tvdef, TypeVarDef)
unique_typevars[name] = TypeVarType(tvdef)
variables.append(tvdef)
rename[tvdef.id] = unique_typevars[name]
assert isinstance(tv, TypeVarType)
unique_typevars[name] = tv
variables.append(tv)
rename[tv.id] = unique_typevars[name]

target = cast(CallableType, expand_type(target, rename))
output.append(target)
Expand Down
6 changes: 3 additions & 3 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from mypy.types import (
Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike,
TypeVarLikeDef, Overloaded, TypeVarType, UnionType, PartialType, TypeOfAny, LiteralType,
TypeVarLikeType, Overloaded, TypeVarType, UnionType, PartialType, TypeOfAny, LiteralType,
DeletedType, NoneType, TypeType, has_type_vars, get_proper_type, ProperType
)
from mypy.nodes import (
Expand Down Expand Up @@ -695,7 +695,7 @@ def analyze_class_attribute_access(itype: Instance,
name: str,
mx: MemberContext,
override_info: Optional[TypeInfo] = None,
original_vars: Optional[Sequence[TypeVarLikeDef]] = None
original_vars: Optional[Sequence[TypeVarLikeType]] = None
) -> Optional[Type]:
"""Analyze access to an attribute on a class object.
Expand Down Expand Up @@ -858,7 +858,7 @@ def analyze_enum_class_attribute_access(itype: Instance,
def add_class_tvars(t: ProperType, isuper: Optional[Instance],
is_classmethod: bool,
original_type: Type,
original_vars: Optional[Sequence[TypeVarLikeDef]] = None) -> Type:
original_vars: Optional[Sequence[TypeVarLikeType]] = None) -> Type:
"""Instantiate type variables during analyze_class_attribute_access,
e.g T and Q in the following:
Expand Down
20 changes: 10 additions & 10 deletions mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Type, Instance, CallableType, TypeGuardType, TypeVisitor, UnboundType, AnyType,
NoneType, TypeVarType, Overloaded, TupleType, TypedDictType, UnionType,
ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, TypeVarId,
FunctionLike, TypeVarDef, LiteralType, get_proper_type, ProperType,
TypeAliasType, ParamSpecDef
FunctionLike, TypeVarType, LiteralType, get_proper_type, ProperType,
TypeAliasType, ParamSpecType
)


Expand Down Expand Up @@ -38,17 +38,17 @@ def freshen_function_type_vars(callee: F) -> F:
if isinstance(callee, CallableType):
if not callee.is_generic():
return cast(F, callee)
tvdefs = []
tvs = []
tvmap: Dict[TypeVarId, Type] = {}
for v in callee.variables:
# TODO(shantanu): fix for ParamSpecDef
if isinstance(v, ParamSpecDef):
# TODO(shantanu): fix for ParamSpecType
if isinstance(v, ParamSpecType):
continue
assert isinstance(v, TypeVarDef)
tvdef = TypeVarDef.new_unification_variable(v)
tvdefs.append(tvdef)
tvmap[v.id] = TypeVarType(tvdef)
fresh = cast(CallableType, expand_type(callee, tvmap)).copy_modified(variables=tvdefs)
assert isinstance(v, TypeVarType)
tv = TypeVarType.new_unification_variable(v)
tvs.append(tv)
tvmap[v.id] = tv
fresh = cast(CallableType, expand_type(callee, tvmap)).copy_modified(variables=tvs)
return cast(F, fresh)
else:
assert isinstance(callee, Overloaded)
Expand Down
4 changes: 2 additions & 2 deletions mypy/fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from mypy.types import (
CallableType, Instance, Overloaded, TupleType, TypeGuardType, TypedDictType,
TypeVarType, UnboundType, UnionType, TypeVisitor, LiteralType,
TypeType, NOT_READY, TypeAliasType, AnyType, TypeOfAny, TypeVarDef
TypeType, NOT_READY, TypeAliasType, AnyType, TypeOfAny
)
from mypy.visitor import NodeVisitor
from mypy.lookup import lookup_fully_qualified
Expand Down Expand Up @@ -184,7 +184,7 @@ def visit_callable_type(self, ct: CallableType) -> None:
if ct.ret_type is not None:
ct.ret_type.accept(self)
for v in ct.variables:
if isinstance(v, TypeVarDef):
if isinstance(v, TypeVarType):
if v.values:
for val in v.values:
val.accept(self)
Expand Down
6 changes: 3 additions & 3 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from mypy.errors import Errors
from mypy.types import (
Type, CallableType, Instance, TypeVarType, TupleType, TypedDictType, LiteralType,
UnionType, NoneType, AnyType, Overloaded, FunctionLike, DeletedType, TypeType, TypeVarDef,
UnionType, NoneType, AnyType, Overloaded, FunctionLike, DeletedType, TypeType, TypeVarType,
UninhabitedType, TypeOfAny, UnboundType, PartialType, get_proper_type, ProperType,
get_proper_types
)
Expand Down Expand Up @@ -1971,7 +1971,7 @@ def [T <: int] f(self, x: int, y: T) -> None
if tp.variables:
tvars = []
for tvar in tp.variables:
if isinstance(tvar, TypeVarDef):
if isinstance(tvar, TypeVarType):
upper_bound = get_proper_type(tvar.upper_bound)
if (isinstance(upper_bound, Instance) and
upper_bound.type.fullname != 'builtins.object'):
Expand All @@ -1983,7 +1983,7 @@ def [T <: int] f(self, x: int, y: T) -> None
else:
tvars.append(tvar.name)
else:
# For other TypeVarLikeDefs, just use the repr
# For other TypeVarLikeTypes, just use the repr
tvars.append(repr(tvar))
s = '[{}] {}'.format(', '.join(tvars), s)
return 'def {}'.format(s)
Expand Down
7 changes: 3 additions & 4 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,11 +921,10 @@ def deserialize(cls, data: JsonDict) -> 'Var':

class ClassDef(Statement):
"""Class definition"""

name: str # Name of the class without module prefix
fullname: Bogus[str] = None # type: ignore # Fully qualified name of the class
defs: "Block"
type_vars: List["mypy.types.TypeVarDef"]
type_vars: List["mypy.types.TypeVarType"]
# Base class expressions (not semantically analyzed -- can be arbitrary expressions)
base_type_exprs: List[Expression]
# Special base classes like Generic[...] get moved here during semantic analysis
Expand All @@ -940,7 +939,7 @@ class ClassDef(Statement):
def __init__(self,
name: str,
defs: 'Block',
type_vars: Optional[List['mypy.types.TypeVarDef']] = None,
type_vars: Optional[List['mypy.types.TypeVarType']] = None,
base_type_exprs: Optional[List[Expression]] = None,
metaclass: Optional[Expression] = None,
keywords: Optional[List[Tuple[str, Expression]]] = None) -> None:
Expand Down Expand Up @@ -975,7 +974,7 @@ def deserialize(self, data: JsonDict) -> 'ClassDef':
assert data['.class'] == 'ClassDef'
res = ClassDef(data['name'],
Block([]),
[mypy.types.TypeVarDef.deserialize(v) for v in data['type_vars']],
[mypy.types.TypeVarType.deserialize(v) for v in data['type_vars']],
)
res.fullname = data['fullname']
return res
Expand Down
Loading

0 comments on commit 2736edb

Please sign in to comment.