Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Self Type #2193

Merged
merged 56 commits into from
Oct 27, 2016
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
04934c3
move things around
elazarg Sep 28, 2016
4eb5f75
first test pass. no override yet
elazarg Sep 29, 2016
181ff09
selftypeClass test pass
elazarg Sep 29, 2016
25e104a
Simple override
elazarg Sep 29, 2016
5e72623
Simple override
elazarg Sep 29, 2016
29d2e0f
test super() and rename self_type()
elazarg Sep 29, 2016
4d5d479
more tests
elazarg Sep 29, 2016
ea6c485
unneeded changes
elazarg Sep 29, 2016
00596a6
merge
elazarg Sep 29, 2016
39615c2
some more tests
elazarg Sep 29, 2016
f8875f9
recursive instantiation
elazarg Sep 29, 2016
9ae3bf9
add implicit bound
elazarg Sep 30, 2016
b29bf87
add tests: prohibit overriding without selftype
elazarg Sep 30, 2016
f097967
Merge
elazarg Sep 30, 2016
747c5b2
Merge
elazarg Sep 30, 2016
9b68a2f
minor
elazarg Sep 30, 2016
65d6428
Merge
elazarg Sep 30, 2016
6c161b9
fix comment
elazarg Sep 30, 2016
1cae034
rename: self->cls
elazarg Oct 1, 2016
733edc1
Fix tests, separate binding from the classes
elazarg Oct 4, 2016
91946e2
Merge upstream into self_type
elazarg Oct 4, 2016
abcb094
add Guido's test
elazarg Oct 5, 2016
9f831c4
move tests to dedicated file
elazarg Oct 7, 2016
aade1ed
move tests to dedicated file
elazarg Oct 7, 2016
a6a0a3b
Merge upstream
elazarg Oct 7, 2016
8e81051
pass report_type to instantiate
elazarg Oct 7, 2016
a9b0b68
send report_type to classmethod
elazarg Oct 7, 2016
ec0f33a
add missing file
elazarg Oct 7, 2016
09cd7bc
more report type
elazarg Oct 7, 2016
8b242e2
support super()
elazarg Oct 8, 2016
a8be2a6
avoid partial types when testing static access
elazarg Oct 9, 2016
3055ab0
remove comment
elazarg Oct 9, 2016
72af252
Merge remote-tracking branch 'upstream/master' into self_type
elazarg Oct 11, 2016
bb2ac78
use existing machinery; still partial types
elazarg Oct 14, 2016
5f26cac
do not trigger the .erased flag
elazarg Oct 14, 2016
1f44c73
do not trigger the .erased flag
elazarg Oct 14, 2016
fcbcf4b
Fix issue with t.variables. adapt SelfTypeBound
elazarg Oct 14, 2016
860c96a
minor
elazarg Oct 15, 2016
b078d61
some override checking: instantiate with fillvars(self)
elazarg Oct 15, 2016
fc29a3a
Merge remote-tracking branch 'upstream/master' into self_type
elazarg Oct 18, 2016
2299a00
forgotten file
elazarg Oct 18, 2016
137f452
initialize mutable in __init__
elazarg Oct 19, 2016
4bcbf59
Rename report_type, Add docstrings etc.
elazarg Oct 20, 2016
6155c7f
minor doc fix
elazarg Oct 20, 2016
47e5e2f
remove binder hack
elazarg Oct 20, 2016
00ae83d
Merge remote-tracking branch 'upstream/master' into self_type
elazarg Oct 26, 2016
30f847e
example for actual_type
elazarg Oct 26, 2016
c152c20
avoid crush in super(); document
elazarg Oct 26, 2016
4f2017e
lint
elazarg Oct 26, 2016
fddbba0
comment for fill_typevars
elazarg Oct 26, 2016
3cf8ecf
comment for fill_typevars
elazarg Oct 26, 2016
707da4e
rename actual_self, use declared_type in analyze_super
elazarg Oct 26, 2016
48e139d
test for erased types
elazarg Oct 26, 2016
25b84f8
lint
elazarg Oct 26, 2016
8944ff1
Revert accidental deletion from doc
elazarg Oct 27, 2016
9221001
add warning for unsafe testcase
elazarg Oct 27, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 25 additions & 22 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,24 @@
DictionaryComprehension, ComplexExpr, EllipsisExpr, TypeAliasExpr,
RefExpr, YieldExpr, BackquoteExpr, ImportFrom, ImportAll, ImportBase,
AwaitExpr,
CONTRAVARIANT, COVARIANT
)
from mypy.nodes import function_type, method_type, method_type_with_fallback
CONTRAVARIANT, COVARIANT)
from mypy import nodes
from mypy.types import (
Type, AnyType, CallableType, Void, FunctionLike, Overloaded, TupleType,
Instance, NoneTyp, ErrorType, strip_type,
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType,
true_only, false_only
true_only, false_only, function_type
)
from mypy.sametypes import is_same_type
from mypy.messages import MessageBuilder
import mypy.checkexpr
from mypy.checkmember import map_type_from_supertype
from mypy.checkmember import map_type_from_supertype, bind_self
from mypy import messages
from mypy.subtypes import (
is_subtype, is_equivalent, is_proper_subtype,
is_more_precise, restrict_subtype_away
is_subtype, is_equivalent, is_proper_subtype, is_more_precise, restrict_subtype_away
)
from mypy.maptype import map_instance_to_supertype
from mypy.semanal import self_type, set_callable_name, refers_to_fullname
from mypy.semanal import fill_typevars, set_callable_name, refers_to_fullname
from mypy.erasetype import erase_typevars
from mypy.expandtype import expand_type
from mypy.visitor import NodeVisitor
Expand Down Expand Up @@ -93,6 +90,11 @@ class TypeChecker(NodeVisitor[Type]):
# Helper for type checking expressions
expr_checker = None # type: mypy.checkexpr.ExpressionChecker

# Class context for checking overriding of a method of the form
# def foo(self: T) -> T
# We need to pass the current class definition for instantiation of T
class_context = None # type: List[Type]

# Stack of function return types
return_types = None # type: List[Type]
# Type context for type inference
Expand Down Expand Up @@ -136,6 +138,7 @@ def __init__(self, errors: Errors, modules: Dict[str, MypyFile], options: Option
self.path = path
self.msg = MessageBuilder(errors, modules)
self.expr_checker = mypy.checkexpr.ExpressionChecker(self, self.msg)
self.class_context = []
self.binder = ConditionalTypeBinder()
self.globals = tree.names
self.return_types = []
Expand Down Expand Up @@ -602,11 +605,13 @@ def is_implicit_any(t: Type) -> bool:
arg_type = typ.arg_types[i]

# Refuse covariant parameter type variables
# TODO: check recuresively for inner type variables
if isinstance(arg_type, TypeVarType):
if arg_type.variance == COVARIANT:
self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT,
arg_type)

if i > 0:
if arg_type.variance == COVARIANT:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment about needing to the type variable check recursively for the argument types. For example, Iteratble[T] is not a valid argument type if T is covariant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, though I am not entirely sure I understood

self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT,
arg_type)
# FIX: if i == 0 and this is not a method then same as above
if typ.arg_kinds[i] == nodes.ARG_STAR:
# builtins.tuple[T] is typing.Tuple[T, ...]
arg_type = self.named_generic_type('builtins.tuple',
Expand Down Expand Up @@ -788,11 +793,11 @@ def check_inplace_operator_method(self, defn: FuncBase) -> None:
method = defn.name()
if method not in nodes.inplace_operator_methods:
return
typ = self.method_type(defn)
typ = bind_self(self.function_type(defn))
cls = defn.info
other_method = '__' + method[3:]
if cls.has_readable_member(other_method):
instance = self_type(cls)
instance = fill_typevars(cls)
typ2 = self.expr_checker.analyze_external_member_access(
other_method, instance, defn)
fail = False
Expand Down Expand Up @@ -868,7 +873,7 @@ def check_method_override_for_base_with_name(
# The name of the method is defined in the base class.

# Construct the type of the overriding method.
typ = self.method_type(defn)
typ = bind_self(self.function_type(defn), self.class_context[-1])
# Map the overridden method type to subtype context so that
# it can be checked for compatibility.
original_type = base_attr.type
Expand All @@ -881,7 +886,7 @@ def check_method_override_for_base_with_name(
assert False, str(base_attr.node)
if isinstance(original_type, FunctionLike):
original = map_type_from_supertype(
method_type(original_type),
bind_self(original_type, self.class_context[-1]),
defn.info, base)
# Check that the types are compatible.
# TODO overloaded signatures
Expand Down Expand Up @@ -965,7 +970,9 @@ def visit_class_def(self, defn: ClassDef) -> Type:
old_binder = self.binder
self.binder = ConditionalTypeBinder()
with self.binder.top_frame_context():
self.class_context.append(fill_typevars(defn.info))
self.accept(defn.defs)
self.class_context.pop()
self.binder = old_binder
if not defn.has_incompatible_baseclass:
# Otherwise we've already found errors; more errors are not useful
Expand Down Expand Up @@ -1012,8 +1019,8 @@ def check_compatibility(self, name: str, base1: TypeInfo,
if (isinstance(first_type, FunctionLike) and
isinstance(second_type, FunctionLike)):
# Method override
first_sig = method_type(first_type)
second_sig = method_type(second_type)
first_sig = bind_self(first_type)
second_sig = bind_self(second_type)
ok = is_subtype(first_sig, second_sig)
elif first_type and second_type:
ok = is_equivalent(first_type, second_type)
Expand Down Expand Up @@ -2335,9 +2342,6 @@ def iterable_item_type(self, instance: Instance) -> Type:
def function_type(self, func: FuncBase) -> FunctionLike:
return function_type(func, self.named_type('builtins.function'))

def method_type(self, func: FuncBase) -> FunctionLike:
return method_type_with_fallback(func, self.named_type('builtins.function'))

# TODO: These next two functions should refer to TypeMap below
def find_isinstance_check(self, n: Expression) -> Tuple[Optional[Dict[Expression, Type]],
Optional[Dict[Expression, Type]]]:
Expand All @@ -2350,7 +2354,6 @@ def push_type_map(self, type_map: Optional[Dict[Expression, Type]]) -> None:
for expr, type in type_map.items():
self.binder.push(expr, type)


# Data structure returned by find_isinstance_check representing
# information learned from the truth or falsehood of a condition. The
# dict maps nodes representing expressions like 'a[0].x' to their
Expand Down
20 changes: 13 additions & 7 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef,
TupleType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType,
PartialType, DeletedType, UnboundType, UninhabitedType, TypeType,
true_only, false_only, is_named_instance
true_only, false_only, is_named_instance, function_type
)
from mypy.nodes import (
NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr,
Expand All @@ -18,7 +18,6 @@
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr,
TypeAliasExpr, BackquoteExpr, ARG_POS, ARG_NAMED, ARG_STAR2, MODULE_REF,
)
from mypy.nodes import function_type
from mypy import nodes
import mypy.checker
from mypy import types
Expand All @@ -32,7 +31,7 @@
from mypy import applytype
from mypy import erasetype
from mypy.checkmember import analyze_member_access, type_object_type
from mypy.semanal import self_type
from mypy.semanal import fill_typevars
from mypy.constraints import get_actual_type
from mypy.checkstrformat import StringFormatterChecker
from mypy.expandtype import expand_type
Expand Down Expand Up @@ -1609,10 +1608,17 @@ def analyze_super(self, e: SuperExpr, is_lvalue: bool) -> Type:
return AnyType()
if not self.chk.in_checked_function():
return AnyType()
return analyze_member_access(e.name, self_type(e.info), e,
is_lvalue, True, False,
self.named_type, self.not_ready_callback,
self.msg, base, chk=self.chk)
# fill_typevars(e.info) erases type variables
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment isn't right. fill_typevars adds type variables, erasure means something like stripping away type variables or replacing them with the upper bound / Any, which isn't happening here. If I have a class define as class C(Generic[T]): then fill_typevars will create a type C[T] when called with the type info for C as an argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I did get it wrong there. Yet if I pass fill_typevars() as actual_type I get the erasure on access.

I'm completely confused now :\

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. wait. it's only confusing because we talk about different variables... sorry. I'll update.

erased_self = fill_typevars(e.info)
args = self.chk.function_stack[-1].arguments
# An empty args with super() is an error, but we need something in declared_self
declared_self = args[0].variable.type if args else erased_self
return analyze_member_access(name=e.name, typ=erased_self, node=e,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the checker tests pass if I pass declared_self as the typ argument. Can we just get rid of erased_self here, as I don't see any use for it? We can return Any without calling analyze_member_access in case there are no arguments (worth generating an error message, though). This would help resolve the actual_self documentation issue in analyze_member_access as well, I believe, as we can rename the argument to original_type and it doesn't need to be special cased for this particular call site. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this will not break for generic classes. But perhaps we can wait and see.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it could well break for generic classes, but as we don't have tests for them we don't know whether they work right now. Let's do that separately. Even for generic classes this is perhaps not the best approach.

is_lvalue=False, is_super=True, is_operator=False,
builtin_type=self.named_type,
not_ready_callback=self.not_ready_callback,
msg=self.msg, override_info=base, chk=self.chk,
actual_self=declared_self)
else:
# Invalid super. This has been reported by the semantic analyzer.
return AnyType()
Expand Down
Loading