Skip to content

Commit 1114bfe

Browse files
committed
Replace class/function distinction with plain/metavariable distinction
Fixes python#603.
1 parent 7ac9330 commit 1114bfe

File tree

4 files changed

+62
-20
lines changed

4 files changed

+62
-20
lines changed

mypy/checkexpr.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from mypy.types import (
66
Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef,
7-
TupleType, Instance, TypeVarType, ErasedType, UnionType,
7+
TupleType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType,
88
PartialType, DeletedType, UnboundType
99
)
1010
from mypy.nodes import (
@@ -22,7 +22,7 @@
2222
import mypy.checker
2323
from mypy import types
2424
from mypy.sametypes import is_same_type
25-
from mypy.erasetype import replace_func_type_vars
25+
from mypy.erasetype import replace_meta_vars
2626
from mypy.messages import MessageBuilder
2727
from mypy import messages
2828
from mypy.infer import infer_type_arguments, infer_function_type_arguments
@@ -34,6 +34,7 @@
3434
from mypy.semanal import self_type
3535
from mypy.constraints import get_actual_type
3636
from mypy.checkstrformat import StringFormatterChecker
37+
from mypy.expandtype import expand_type
3738

3839

3940
# Type of callback user for checking individual function arguments. See
@@ -226,6 +227,7 @@ def check_call(self, callee: Type, args: List[Node],
226227
lambda i: self.accept(args[i]))
227228

228229
if callee.is_generic():
230+
callee = freshen_generic_callable(callee)
229231
callee = self.infer_function_type_arguments_using_context(
230232
callee, context)
231233
callee = self.infer_function_type_arguments(
@@ -346,12 +348,12 @@ def infer_function_type_arguments_using_context(
346348
ctx = self.chk.type_context[-1]
347349
if not ctx:
348350
return callable
349-
# The return type may have references to function type variables that
351+
# The return type may have references to type metavariables that
350352
# we are inferring right now. We must consider them as indeterminate
351353
# and they are not potential results; thus we replace them with the
352354
# special ErasedType type. On the other hand, class type variables are
353355
# valid results.
354-
erased_ctx = replace_func_type_vars(ctx, ErasedType())
356+
erased_ctx = replace_meta_vars(ctx, ErasedType())
355357
ret_type = callable.ret_type
356358
if isinstance(ret_type, TypeVarType):
357359
if ret_type.values or (not isinstance(ctx, Instance) or
@@ -1295,7 +1297,7 @@ def infer_lambda_type_using_context(self, e: FuncExpr) -> CallableType:
12951297
# they must be considered as indeterminate. We use ErasedType since it
12961298
# does not affect type inference results (it is for purposes like this
12971299
# only).
1298-
ctx = replace_func_type_vars(ctx, ErasedType())
1300+
ctx = replace_meta_vars(ctx, ErasedType())
12991301

13001302
callable_ctx = cast(CallableType, ctx)
13011303

@@ -1709,3 +1711,14 @@ def overload_arg_similarity(actual: Type, formal: Type) -> int:
17091711
return 2
17101712
# Fall back to a conservative equality check for the remaining kinds of type.
17111713
return 2 if is_same_type(erasetype.erase_type(actual), erasetype.erase_type(formal)) else 0
1714+
1715+
1716+
def freshen_generic_callable(callee: CallableType) -> CallableType:
1717+
tvdefs = []
1718+
tvmap = {} # type: Dict[TypeVarId, Type]
1719+
for v in callee.variables:
1720+
tvdef = TypeVarDef.new_unification_variable(v)
1721+
tvdefs.append(tvdef)
1722+
tvmap[v.id] = TypeVarType(tvdef)
1723+
1724+
return cast(CallableType, expand_type(callee, tvmap)).copy_modified(variables=tvdefs)

mypy/erasetype.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ def erase_id(id: TypeVarId) -> bool:
109109
return t.accept(TypeVarEraser(erase_id, AnyType()))
110110

111111

112-
def replace_func_type_vars(t: Type, target_type: Type) -> Type:
113-
"""Replace function type variables in a type with the target type."""
114-
return t.accept(TypeVarEraser(lambda id: id.is_func_var(), target_type))
112+
def replace_meta_vars(t: Type, target_type: Type) -> Type:
113+
"""Replace unification variables in a type with the target type."""
114+
return t.accept(TypeVarEraser(lambda id: id.is_meta_var(), target_type))
115115

116116

117117
class TypeVarEraser(TypeTranslator):

mypy/test/data/check-inference-context.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,3 +748,11 @@ def f2(iterable: Iterable[Tuple[str, Any]], **kw: Any) -> None:
748748
pass
749749
[builtins fixtures/dict.py]
750750
[out]
751+
752+
[case testInferenceInGenericFunction]
753+
from typing import TypeVar, List
754+
T = TypeVar('T')
755+
def f(a: T) -> None:
756+
l = [] # type: List[T]
757+
l.append(a)
758+
[builtins fixtures/list.py]

mypy/types.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from abc import abstractmethod
44
from typing import (
5-
Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Sequence, Optional
5+
Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Sequence, Optional, Union
66
)
77

88
import mypy.nodes
@@ -47,32 +47,43 @@ def deserialize(cls, data: JsonDict) -> 'Type':
4747

4848
class TypeVarId:
4949
# 1, 2, ... for type-related, -1, ... for function-related
50-
5150
raw_id = 0 # type: int
5251

53-
def __init__(self, raw_id: int) -> None:
52+
# Level of the variable in type inference. Currently either 0 for
53+
# declared types, or 1 for type inference unification variables.
54+
meta_level = 0 # type: int
55+
56+
# Used for allocating fresh ids
57+
next_raw_id = 1 # type: int
58+
59+
def __init__(self, raw_id: int, meta_level: int = 0) -> None:
5460
self.raw_id = raw_id
61+
self.meta_level = meta_level
62+
63+
@staticmethod
64+
def new(meta_level: int) -> 'TypeVarId':
65+
raw_id = TypeVarId.next_raw_id
66+
TypeVarId.next_raw_id += 1
67+
return TypeVarId(raw_id, meta_level)
5568

5669
def __repr__(self) -> str:
5770
return self.raw_id.__repr__()
5871

5972
def __eq__(self, other: object) -> bool:
6073
if isinstance(other, TypeVarId):
61-
return self.raw_id == other.raw_id
74+
return (self.raw_id == other.raw_id and
75+
self.meta_level == other.meta_level)
6276
else:
6377
return False
6478

6579
def __ne__(self, other: object) -> bool:
6680
return not (self == other)
6781

6882
def __hash__(self) -> int:
69-
return hash(self.raw_id)
70-
71-
def is_class_var(self) -> bool:
72-
return self.raw_id > 0
83+
return hash((self.raw_id, self.meta_level))
7384

74-
def is_func_var(self) -> bool:
75-
return self.raw_id < 0
85+
def is_meta_var(self) -> bool:
86+
return self.meta_level > 0
7687

7788

7889
class TypeVarDef(mypy.nodes.Context):
@@ -85,15 +96,23 @@ class TypeVarDef(mypy.nodes.Context):
8596
variance = INVARIANT # type: int
8697
line = 0
8798

88-
def __init__(self, name: str, raw_id: int, values: Optional[List[Type]],
99+
def __init__(self, name: str, id: Union[TypeVarId, int], values: Optional[List[Type]],
89100
upper_bound: Type, variance: int = INVARIANT, line: int = -1) -> None:
90101
self.name = name
91-
self.id = TypeVarId(raw_id)
102+
if isinstance(id, int):
103+
id = TypeVarId(id)
104+
self.id = id
92105
self.values = values
93106
self.upper_bound = upper_bound
94107
self.variance = variance
95108
self.line = line
96109

110+
@staticmethod
111+
def new_unification_variable(old: 'TypeVarDef') -> 'TypeVarDef':
112+
new_id = TypeVarId.new(meta_level=1)
113+
return TypeVarDef(old.name, new_id, old.values,
114+
old.upper_bound, old.variance, old.line)
115+
97116
def get_line(self) -> int:
98117
return self.line
99118

@@ -106,6 +125,7 @@ def __repr__(self) -> str:
106125
return self.name
107126

108127
def serialize(self) -> JsonDict:
128+
assert not self.id.is_meta_var()
109129
return {'.class': 'TypeVarDef',
110130
'name': self.name,
111131
'id': self.id.raw_id,
@@ -377,6 +397,7 @@ def erase_to_union_or_bound(self) -> Type:
377397
return self.upper_bound
378398

379399
def serialize(self) -> JsonDict:
400+
assert not self.id.is_meta_var()
380401
return {'.class': 'TypeVarType',
381402
'name': self.name,
382403
'id': self.id.raw_id,

0 commit comments

Comments
 (0)