Skip to content

Commit cc24f5b

Browse files
sixoletJukkaL
authored andcommitted
Support for functions producing generic functions (#3113)
Provides support for returning a generic Callable from a function, allowing you to write a function that produces a decorator, for example. This pattern is common: ``` def deco(x: int) -> Callable[[T], T]: ... @Deco(4) def f(stuff): ... ``` Details on why this touches so much code: Returning a generic Callable from a function requires binding type variables while we're traversing the type analysis phase of the check. Previously, all type variable binding was done from semanal.py. I refactored type variable tracking and binding into its own class, that's used by both semanal.py and typeanal.py to keep track of its type variables. I also, in the process, nixed the thing where we're mutating TypeVarExprs to bind them, instead keeping track of the bindings in the scope object I created. Seems more sustainable in a world where more than one class has to deal with typevar binding.
1 parent c38a836 commit cc24f5b

File tree

7 files changed

+498
-355
lines changed

7 files changed

+498
-355
lines changed

mypy/checkexpr.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, AwaitExpr, YieldExpr,
2222
YieldFromExpr, TypedDictExpr, PromoteExpr, NewTypeExpr, NamedTupleExpr, TypeVarExpr,
2323
TypeAliasExpr, BackquoteExpr, EnumCallExpr,
24-
ARG_POS, ARG_NAMED, ARG_STAR, ARG_STAR2, MODULE_REF,
25-
UNBOUND_TVAR, BOUND_TVAR, LITERAL_TYPE
24+
ARG_POS, ARG_NAMED, ARG_STAR, ARG_STAR2, MODULE_REF, TVAR, LITERAL_TYPE,
2625
)
2726
from mypy import nodes
2827
import mypy.checker
@@ -1623,7 +1622,7 @@ def replace_tvars_any(self, tp: Type) -> Type:
16231622
sym = self.chk.lookup_qualified(arg.name)
16241623
except KeyError:
16251624
pass
1626-
if sym and (sym.kind == UNBOUND_TVAR or sym.kind == BOUND_TVAR):
1625+
if sym and (sym.kind == TVAR):
16271626
new_args[i] = AnyType()
16281627
else:
16291628
new_args[i] = self.replace_tvars_any(arg)

mypy/nodes.py

+6-16
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ def get_column(self) -> int: pass
3939
GDEF = 1 # type: int
4040
MDEF = 2 # type: int
4141
MODULE_REF = 3 # type: int
42-
# Type variable declared using TypeVar(...) has kind UNBOUND_TVAR. It's not
43-
# valid as a type. A type variable is valid as a type (kind BOUND_TVAR) within
42+
# Type variable declared using TypeVar(...) has kind TVAR. It's not
43+
# valid as a type unless bound in a TypeVarScope. That happens within:
4444
# (1) a generic class that uses the type variable as a type argument or
4545
# (2) a generic function that refers to the type variable in its signature.
46-
UNBOUND_TVAR = 4 # type: int
47-
BOUND_TVAR = 5 # type: int
46+
TVAR = 4 # type: int
47+
4848
TYPE_ALIAS = 6 # type: int
4949
# Placeholder for a name imported via 'from ... import'. Second phase of
5050
# semantic will replace this the actual imported reference. This is
@@ -65,8 +65,7 @@ def get_column(self) -> int: pass
6565
GDEF: 'Gdef',
6666
MDEF: 'Mdef',
6767
MODULE_REF: 'ModuleRef',
68-
UNBOUND_TVAR: 'UnboundTvar',
69-
BOUND_TVAR: 'Tvar',
68+
TVAR: 'Tvar',
7069
TYPE_ALIAS: 'TypeAlias',
7170
UNBOUND_IMPORTED: 'UnboundImported',
7271
}
@@ -2211,17 +2210,14 @@ class SymbolTableNode:
22112210
# - LDEF: local definition (of any kind)
22122211
# - GDEF: global (module-level) definition
22132212
# - MDEF: class member definition
2214-
# - UNBOUND_TVAR: TypeVar(...) definition, not bound
2215-
# - TVAR: type variable in a bound scope (generic function / generic clas)
2213+
# - TVAR: TypeVar(...) definition
22162214
# - MODULE_REF: reference to a module
22172215
# - TYPE_ALIAS: type alias
22182216
# - UNBOUND_IMPORTED: temporary kind for imported names
22192217
kind = None # type: int
22202218
# AST node of definition (FuncDef/Var/TypeInfo/Decorator/TypeVarExpr,
22212219
# or None for a bound type variable).
22222220
node = None # type: Optional[SymbolNode]
2223-
# Type variable definition (for bound type variables only)
2224-
tvar_def = None # type: Optional[mypy.types.TypeVarDef]
22252221
# Module id (e.g. "foo.bar") or None
22262222
mod_id = ''
22272223
# If this not None, override the type of the 'node' attribute.
@@ -2237,13 +2233,11 @@ class SymbolTableNode:
22372233

22382234
def __init__(self, kind: int, node: Optional[SymbolNode], mod_id: str = None,
22392235
typ: 'mypy.types.Type' = None,
2240-
tvar_def: 'mypy.types.TypeVarDef' = None,
22412236
module_public: bool = True, normalized: bool = False) -> None:
22422237
self.kind = kind
22432238
self.node = node
22442239
self.type_override = typ
22452240
self.mod_id = mod_id
2246-
self.tvar_def = tvar_def
22472241
self.module_public = module_public
22482242
self.normalized = normalized
22492243

@@ -2287,8 +2281,6 @@ def serialize(self, prefix: str, name: str) -> JsonDict:
22872281
data = {'.class': 'SymbolTableNode',
22882282
'kind': node_kinds[self.kind],
22892283
} # type: JsonDict
2290-
if self.tvar_def:
2291-
data['tvar_def'] = self.tvar_def.serialize()
22922284
if not self.module_public:
22932285
data['module_public'] = False
22942286
if self.kind == MODULE_REF:
@@ -2323,8 +2315,6 @@ def deserialize(cls, data: JsonDict) -> 'SymbolTableNode':
23232315
if 'type_override' in data:
23242316
typ = mypy.types.deserialize_type(data['type_override'])
23252317
stnode = SymbolTableNode(kind, node, typ=typ)
2326-
if 'tvar_def' in data:
2327-
stnode.tvar_def = mypy.types.TypeVarDef.deserialize(data['tvar_def'])
23282318
if 'module_public' in data:
23292319
stnode.module_public = data['module_public']
23302320
return stnode

0 commit comments

Comments
 (0)