Skip to content

Commit ba85545

Browse files
elazargJukkaL
authored andcommitted
Self Type (#2193)
Add preliminary support for annotating the self argument as a type variable or Type[T].
1 parent 1fb67d0 commit ba85545

13 files changed

+493
-168
lines changed

mypy/checker.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,24 @@
2424
DictionaryComprehension, ComplexExpr, EllipsisExpr, TypeAliasExpr,
2525
RefExpr, YieldExpr, BackquoteExpr, ImportFrom, ImportAll, ImportBase,
2626
AwaitExpr,
27-
CONTRAVARIANT, COVARIANT
28-
)
29-
from mypy.nodes import function_type, method_type, method_type_with_fallback
27+
CONTRAVARIANT, COVARIANT)
3028
from mypy import nodes
3129
from mypy.types import (
3230
Type, AnyType, CallableType, Void, FunctionLike, Overloaded, TupleType,
3331
Instance, NoneTyp, ErrorType, strip_type,
3432
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType,
35-
true_only, false_only
33+
true_only, false_only, function_type
3634
)
3735
from mypy.sametypes import is_same_type
3836
from mypy.messages import MessageBuilder
3937
import mypy.checkexpr
40-
from mypy.checkmember import map_type_from_supertype
38+
from mypy.checkmember import map_type_from_supertype, bind_self
4139
from mypy import messages
4240
from mypy.subtypes import (
43-
is_subtype, is_equivalent, is_proper_subtype,
44-
is_more_precise, restrict_subtype_away
41+
is_subtype, is_equivalent, is_proper_subtype, is_more_precise, restrict_subtype_away
4542
)
4643
from mypy.maptype import map_instance_to_supertype
47-
from mypy.semanal import self_type, set_callable_name, refers_to_fullname
44+
from mypy.semanal import fill_typevars, set_callable_name, refers_to_fullname
4845
from mypy.erasetype import erase_typevars
4946
from mypy.expandtype import expand_type
5047
from mypy.visitor import NodeVisitor
@@ -93,6 +90,11 @@ class TypeChecker(NodeVisitor[Type]):
9390
# Helper for type checking expressions
9491
expr_checker = None # type: mypy.checkexpr.ExpressionChecker
9592

93+
# Class context for checking overriding of a method of the form
94+
# def foo(self: T) -> T
95+
# We need to pass the current class definition for instantiation of T
96+
class_context = None # type: List[Type]
97+
9698
# Stack of function return types
9799
return_types = None # type: List[Type]
98100
# Type context for type inference
@@ -136,6 +138,7 @@ def __init__(self, errors: Errors, modules: Dict[str, MypyFile], options: Option
136138
self.path = path
137139
self.msg = MessageBuilder(errors, modules)
138140
self.expr_checker = mypy.checkexpr.ExpressionChecker(self, self.msg)
141+
self.class_context = []
139142
self.binder = ConditionalTypeBinder()
140143
self.globals = tree.names
141144
self.return_types = []
@@ -602,11 +605,13 @@ def is_implicit_any(t: Type) -> bool:
602605
arg_type = typ.arg_types[i]
603606

604607
# Refuse covariant parameter type variables
608+
# TODO: check recuresively for inner type variables
605609
if isinstance(arg_type, TypeVarType):
606-
if arg_type.variance == COVARIANT:
607-
self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT,
608-
arg_type)
609-
610+
if i > 0:
611+
if arg_type.variance == COVARIANT:
612+
self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT,
613+
arg_type)
614+
# FIX: if i == 0 and this is not a method then same as above
610615
if typ.arg_kinds[i] == nodes.ARG_STAR:
611616
# builtins.tuple[T] is typing.Tuple[T, ...]
612617
arg_type = self.named_generic_type('builtins.tuple',
@@ -788,11 +793,11 @@ def check_inplace_operator_method(self, defn: FuncBase) -> None:
788793
method = defn.name()
789794
if method not in nodes.inplace_operator_methods:
790795
return
791-
typ = self.method_type(defn)
796+
typ = bind_self(self.function_type(defn))
792797
cls = defn.info
793798
other_method = '__' + method[3:]
794799
if cls.has_readable_member(other_method):
795-
instance = self_type(cls)
800+
instance = fill_typevars(cls)
796801
typ2 = self.expr_checker.analyze_external_member_access(
797802
other_method, instance, defn)
798803
fail = False
@@ -868,7 +873,7 @@ def check_method_override_for_base_with_name(
868873
# The name of the method is defined in the base class.
869874

870875
# Construct the type of the overriding method.
871-
typ = self.method_type(defn)
876+
typ = bind_self(self.function_type(defn), self.class_context[-1])
872877
# Map the overridden method type to subtype context so that
873878
# it can be checked for compatibility.
874879
original_type = base_attr.type
@@ -881,7 +886,7 @@ def check_method_override_for_base_with_name(
881886
assert False, str(base_attr.node)
882887
if isinstance(original_type, FunctionLike):
883888
original = map_type_from_supertype(
884-
method_type(original_type),
889+
bind_self(original_type, self.class_context[-1]),
885890
defn.info, base)
886891
# Check that the types are compatible.
887892
# TODO overloaded signatures
@@ -965,7 +970,9 @@ def visit_class_def(self, defn: ClassDef) -> Type:
965970
old_binder = self.binder
966971
self.binder = ConditionalTypeBinder()
967972
with self.binder.top_frame_context():
973+
self.class_context.append(fill_typevars(defn.info))
968974
self.accept(defn.defs)
975+
self.class_context.pop()
969976
self.binder = old_binder
970977
if not defn.has_incompatible_baseclass:
971978
# Otherwise we've already found errors; more errors are not useful
@@ -1012,8 +1019,8 @@ def check_compatibility(self, name: str, base1: TypeInfo,
10121019
if (isinstance(first_type, FunctionLike) and
10131020
isinstance(second_type, FunctionLike)):
10141021
# Method override
1015-
first_sig = method_type(first_type)
1016-
second_sig = method_type(second_type)
1022+
first_sig = bind_self(first_type)
1023+
second_sig = bind_self(second_type)
10171024
ok = is_subtype(first_sig, second_sig)
10181025
elif first_type and second_type:
10191026
ok = is_equivalent(first_type, second_type)
@@ -2335,9 +2342,6 @@ def iterable_item_type(self, instance: Instance) -> Type:
23352342
def function_type(self, func: FuncBase) -> FunctionLike:
23362343
return function_type(func, self.named_type('builtins.function'))
23372344

2338-
def method_type(self, func: FuncBase) -> FunctionLike:
2339-
return method_type_with_fallback(func, self.named_type('builtins.function'))
2340-
23412345
# TODO: These next two functions should refer to TypeMap below
23422346
def find_isinstance_check(self, n: Expression) -> Tuple[Optional[Dict[Expression, Type]],
23432347
Optional[Dict[Expression, Type]]]:
@@ -2350,7 +2354,6 @@ def push_type_map(self, type_map: Optional[Dict[Expression, Type]]) -> None:
23502354
for expr, type in type_map.items():
23512355
self.binder.push(expr, type)
23522356

2353-
23542357
# Data structure returned by find_isinstance_check representing
23552358
# information learned from the truth or falsehood of a condition. The
23562359
# dict maps nodes representing expressions like 'a[0].x' to their

mypy/checkexpr.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef,
77
TupleType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType,
88
PartialType, DeletedType, UnboundType, UninhabitedType, TypeType,
9-
true_only, false_only, is_named_instance
9+
true_only, false_only, is_named_instance, function_type
1010
)
1111
from mypy.nodes import (
1212
NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr,
@@ -18,7 +18,6 @@
1818
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr,
1919
TypeAliasExpr, BackquoteExpr, ARG_POS, ARG_NAMED, ARG_STAR2, MODULE_REF,
2020
)
21-
from mypy.nodes import function_type
2221
from mypy import nodes
2322
import mypy.checker
2423
from mypy import types
@@ -32,7 +31,6 @@
3231
from mypy import applytype
3332
from mypy import erasetype
3433
from mypy.checkmember import analyze_member_access, type_object_type
35-
from mypy.semanal import self_type
3634
from mypy.constraints import get_actual_type
3735
from mypy.checkstrformat import StringFormatterChecker
3836
from mypy.expandtype import expand_type
@@ -1605,10 +1603,18 @@ def analyze_super(self, e: SuperExpr, is_lvalue: bool) -> Type:
16051603
return AnyType()
16061604
if not self.chk.in_checked_function():
16071605
return AnyType()
1608-
return analyze_member_access(e.name, self_type(e.info), e,
1609-
is_lvalue, True, False,
1610-
self.named_type, self.not_ready_callback,
1611-
self.msg, base, chk=self.chk)
1606+
args = self.chk.function_stack[-1].arguments
1607+
# An empty args with super() is an error; we need something in declared_self
1608+
if not args:
1609+
self.chk.fail('super() requires at least on positional argument', e)
1610+
return AnyType()
1611+
declared_self = args[0].variable.type
1612+
return analyze_member_access(name=e.name, typ=declared_self, node=e,
1613+
is_lvalue=False, is_super=True, is_operator=False,
1614+
builtin_type=self.named_type,
1615+
not_ready_callback=self.not_ready_callback,
1616+
msg=self.msg, override_info=base, chk=self.chk,
1617+
original_type=declared_self)
16121618
else:
16131619
# Invalid super. This has been reported by the semantic analyzer.
16141620
return AnyType()

0 commit comments

Comments
 (0)