Skip to content

Self Type #2193

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

Merged
merged 56 commits into from
Oct 27, 2016
Merged
Show file tree
Hide file tree
Changes from 17 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
27 changes: 12 additions & 15 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@
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
Expand All @@ -48,7 +46,7 @@
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_by_instance, expand_type
from mypy.visitor import NodeVisitor
Expand Down Expand Up @@ -592,7 +590,9 @@ def is_implicit_any(t: Type) -> bool:
if arg_type.variance == COVARIANT:
self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT,
arg_type)

# TODO:
# if arg_type.variance == SELF_VARIANCE and i > 0:
# self.fail("Cannot use bare self type in method arguments", arg_type)
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 @@ -747,11 +747,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 = self.function_type(defn).bind_self()
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 @@ -827,7 +827,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 = self.function_type(defn).bind_self()
# Map the overridden method type to subtype context so that
# it can be checked for compatibility.
original_type = base_attr.type
Expand All @@ -840,7 +840,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),
original_type.bind_self(),
defn.info, base)
# Check that the types are compatible.
# TODO overloaded signatures
Expand Down Expand Up @@ -979,8 +979,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 = first_type.bind_self()
second_sig = second_type.bind_self()
ok = is_subtype(first_sig, second_sig)
elif first_type and second_type:
ok = is_equivalent(first_type, second_type)
Expand Down Expand Up @@ -2354,9 +2354,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'))


# Data structure returned by find_isinstance_check representing
# information learned from the truth or falsehood of a condition. The
Expand Down
7 changes: 3 additions & 4 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 @@ -1608,7 +1607,7 @@ def analyze_super(self, e: SuperExpr, is_lvalue: bool) -> Type:
return AnyType()
if not self.chk.typing_mode_full():
return AnyType()
return analyze_member_access(e.name, self_type(e.info), e,
return analyze_member_access(e.name, fill_typevars(e.info), e,
is_lvalue, True, False,
self.named_type, self.not_ready_callback,
self.msg, base, chk=self.chk)
Expand Down
54 changes: 25 additions & 29 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
"""Type checking of attribute access"""

from typing import cast, Callable, List, Dict, Optional
from typing import cast, Callable, List, Optional, TYPE_CHECKING

from mypy.types import (
Type, Instance, AnyType, TupleType, CallableType, FunctionLike, TypeVarId, TypeVarDef,
Overloaded, TypeVarType, TypeTranslator, UnionType, PartialType,
DeletedType, NoneTyp, TypeType
Type, Instance, AnyType, TupleType, CallableType, FunctionLike, TypeVarDef,
Overloaded, TypeVarType, UnionType, PartialType,
DeletedType, NoneTyp, TypeType, function_type
)
from mypy.nodes import TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile
from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, OpExpr, ComparisonExpr
from mypy.nodes import function_type, Decorator, OverloadedFuncDef
from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2
from mypy.nodes import Decorator, OverloadedFuncDef
from mypy.messages import MessageBuilder
from mypy.maptype import map_instance_to_supertype
from mypy.expandtype import expand_type_by_instance
from mypy.nodes import method_type, method_type_with_fallback
from mypy.semanal import self_type
from mypy.semanal import fill_typevars
from mypy import messages
from mypy import subtypes
if False: # import for forward declaration only
if TYPE_CHECKING: # import for forward declaration only
import mypy.checker


Expand Down Expand Up @@ -64,13 +63,14 @@ def analyze_member_access(name: str,
not_ready_callback)
if is_lvalue:
msg.cant_assign_to_method(node)
typ = map_instance_to_supertype(typ, method.info)
signature = function_type(method, builtin_type('builtins.function'))
if name == '__new__':
# __new__ is special and behaves like a static method -- don't strip
# the first argument.
signature = function_type(method, builtin_type('builtins.function'))
pass
else:
signature = method_type_with_fallback(method, builtin_type('builtins.function'))
signature = signature.bind_self(typ)
typ = map_instance_to_supertype(typ, method.info)
return expand_type_by_instance(signature, typ)
else:
# Not a method.
Expand Down Expand Up @@ -195,9 +195,10 @@ def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo,
if not is_lvalue:
method = info.get_method('__getattr__')
if method:
function = function_type(method, builtin_type('builtins.function'))
bound_method = function.bind_self(itype)
typ = map_instance_to_supertype(itype, method.info)
getattr_type = expand_type_by_instance(
method_type_with_fallback(method, builtin_type('builtins.function')), typ)
getattr_type = expand_type_by_instance(bound_method, typ)
if isinstance(getattr_type, CallableType):
return getattr_type.ret_type

Expand Down Expand Up @@ -245,7 +246,7 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont
# class.
functype = t
check_method_type(functype, itype, var.is_classmethod, node, msg)
signature = method_type(functype)
signature = functype.bind_self()
if var.is_property:
# A property cannot have an overloaded type => the cast
# is fine.
Expand Down Expand Up @@ -343,7 +344,7 @@ def analyze_class_attribute_access(itype: Instance,
if isinstance(t, PartialType):
return handle_partial_attribute_type(t, is_lvalue, msg, node.node)
is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class
return add_class_tvars(t, itype.type, is_classmethod, builtin_type)
return add_class_tvars(t, itype, is_classmethod, builtin_type)
elif isinstance(node.node, Var):
not_ready_callback(name, context)
return AnyType()
Expand All @@ -362,23 +363,18 @@ def analyze_class_attribute_access(itype: Instance,
return function_type(cast(FuncBase, node.node), builtin_type('builtins.function'))


def add_class_tvars(t: Type, info: TypeInfo, is_classmethod: bool,
def add_class_tvars(t: Type, itype: Instance, is_classmethod: bool,
builtin_type: Callable[[str], Instance]) -> Type:
info = itype.type # type: TypeInfo
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add docstring. In particular, describe report_type.

if isinstance(t, CallableType):
# TODO: Should we propagate type variable values?
vars = [TypeVarDef(n, i + 1, None, builtin_type('builtins.object'), tv.variance)
for (i, n), tv in zip(enumerate(info.type_vars), info.defn.type_vars)]
arg_types = t.arg_types
arg_kinds = t.arg_kinds
arg_names = t.arg_names
if is_classmethod:
arg_types = arg_types[1:]
arg_kinds = arg_kinds[1:]
arg_names = arg_names[1:]
return t.copy_modified(arg_types=arg_types, arg_kinds=arg_kinds, arg_names=arg_names,
variables=vars + t.variables)
t = t.bind_self(itype)
return t.copy_modified(variables=vars + t.variables)
elif isinstance(t, Overloaded):
return Overloaded([cast(CallableType, add_class_tvars(i, info, is_classmethod,
return Overloaded([cast(CallableType, add_class_tvars(i, itype, is_classmethod,
builtin_type))
for i in t.items()])
return t
Expand Down Expand Up @@ -423,7 +419,7 @@ def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) ->

def type_object_type_from_function(init_or_new: FuncBase, info: TypeInfo,
fallback: Instance) -> FunctionLike:
signature = method_type_with_fallback(init_or_new, fallback)
signature = function_type(init_or_new, fallback).bind_self()

# The __init__ method might come from a generic superclass
# (init_or_new.info) with type variables that do not map
Expand Down Expand Up @@ -461,7 +457,7 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance,
variables.extend(init_type.variables)

callable_type = init_type.copy_modified(
ret_type=self_type(info), fallback=type_type, name=None, variables=variables,
ret_type=fill_typevars(info), fallback=type_type, name=None, variables=variables,
special_sig=special_sig)
c = callable_type.with_name('"{}"'.format(info.name()))
c.is_classmethod_class = True
Expand All @@ -482,7 +478,7 @@ def map_type_from_supertype(typ: Type, sub_info: TypeInfo,
Now S in the context of D would be mapped to E[T] in the context of C.
"""
# Create the type of self in subtype, of form t[a1, ...].
inst_type = self_type(sub_info)
inst_type = fill_typevars(sub_info)
if isinstance(inst_type, TupleType):
inst_type = inst_type.fallback
# Map the type of self to supertype. This gets us a description of the
Expand Down
2 changes: 1 addition & 1 deletion mypy/constraints.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Type inference constraints."""

from typing import List, Optional, cast
from typing import List, Optional

from mypy.types import (
CallableType, Type, TypeVisitor, UnboundType, AnyType, Void, NoneTyp, TypeVarType,
Expand Down
59 changes: 4 additions & 55 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""Abstract syntax tree node classes (i.e. parse tree)."""

import os
import re
from abc import abstractmethod, ABCMeta
from abc import abstractmethod

from typing import (
Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional
Expand Down Expand Up @@ -129,7 +128,7 @@ def get_column(self) -> int:
return self.column

def accept(self, visitor: NodeVisitor[T]) -> T:
raise RuntimeError('Not implemented')
raise RuntimeError('Not implemented: {}.accept({})'.format(type(self), type(visitor)))


# These are placeholders for a future refactoring; see #1783.
Expand Down Expand Up @@ -1648,9 +1647,10 @@ def accept(self, visitor: NodeVisitor[T]) -> T:
#
# If T is contravariant in Foo[T], Foo[object] is a subtype of
# Foo[int], but not vice versa.
CONTRAVARIANT = -1 # type: int
Copy link
Collaborator

Choose a reason for hiding this comment

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

The change of the constant value seems unrelated to the rest of diff and unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My poor git skills seems to have failed me. I did revert this one.

INVARIANT = 0 # type: int
COVARIANT = 1 # type: int
CONTRAVARIANT = 2 # type: int
# TODO: SELF_VARIANCE = 2 # type: int
Copy link
Contributor Author

@elazarg elazarg Sep 30, 2016

Choose a reason for hiding this comment

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

What do you think? I am not sure what this is, For now simple INVARIANT seems to suffice

Copy link
Collaborator

Choose a reason for hiding this comment

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

INVARIANT should be fine. I don't see a need for special variance.



class TypeVarExpr(SymbolNode, Expression):
Expand Down Expand Up @@ -2229,57 +2229,6 @@ def deserialize(cls, data: JsonDict) -> 'SymbolTable':
return st


def function_type(func: FuncBase, fallback: 'mypy.types.Instance') -> 'mypy.types.FunctionLike':
if func.type:
assert isinstance(func.type, mypy.types.FunctionLike)
return func.type
else:
# Implicit type signature with dynamic types.
# Overloaded functions always have a signature, so func must be an ordinary function.
fdef = cast(FuncDef, func)
name = func.name()
if name:
name = '"{}"'.format(name)

return mypy.types.CallableType(
[mypy.types.AnyType()] * len(fdef.arg_names),
fdef.arg_kinds,
fdef.arg_names,
mypy.types.AnyType(),
fallback,
name,
implicit=True,
)


def method_type_with_fallback(func: FuncBase,
fallback: 'mypy.types.Instance') -> 'mypy.types.FunctionLike':
"""Return the signature of a method (omit self)."""
return method_type(function_type(func, fallback))


def method_type(sig: 'mypy.types.FunctionLike') -> 'mypy.types.FunctionLike':
if isinstance(sig, mypy.types.CallableType):
return method_callable(sig)
else:
sig = cast(mypy.types.Overloaded, sig)
items = [] # type: List[mypy.types.CallableType]
for c in sig.items():
items.append(method_callable(c))
return mypy.types.Overloaded(items)


def method_callable(c: 'mypy.types.CallableType') -> 'mypy.types.CallableType':
if c.arg_kinds and c.arg_kinds[0] == ARG_STAR:
# The signature is of the form 'def foo(*args, ...)'.
# In this case we shouldn't drop the first arg,
# since self will be absorbed by the *args.
return c
return c.copy_modified(arg_types=c.arg_types[1:],
arg_kinds=c.arg_kinds[1:],
arg_names=c.arg_names[1:])


class MroError(Exception):
"""Raised if a consistent mro cannot be determined for a class."""

Expand Down
Loading