Skip to content

Support for covariant and contravariant TypeVar's #694

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 19 commits into from
Aug 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
106bc68
Added support for covariant and contravariant type variables to seman…
spkersten May 19, 2015
1f17da3
Added support for covariant and contravariant type variables to type …
spkersten May 20, 2015
a4f7ab0
Moved the 'variance' attribute from TypeInfo into TypeVarDef.
spkersten May 20, 2015
2ca950a
Take variance into consideration in is_proper_subtype as well.
spkersten May 20, 2015
a43e359
Minimal changes to typing.py (and its stubs) to make it compatible wi…
spkersten May 20, 2015
ae2a35a
Refactored process_typevar_declaration.
spkersten May 31, 2015
9b1c16f
Fixes to meet_type and join_type to make them work (better) with type…
spkersten Jun 6, 2015
064dca8
Fixes to subtypes and types tests: Variance of generic types was not …
spkersten Jun 6, 2015
285b48d
Merge remote-tracking branch 'upstream/master' into variance
spkersten Jun 6, 2015
623a2ac
Fixed type errors.
spkersten Jun 6, 2015
e439d3a
Added subtype tests for contravariant types.
spkersten Jun 7, 2015
7376f90
Added subtype tests for invariant types.
spkersten Jun 7, 2015
7fd8bb5
Updated 3.2/typing.pyi with covariant type vars for immutable sequences.
spkersten Jun 7, 2015
142098d
Updated 2.7/typing.pyi with covariant type vars for immutable sequences.
spkersten Jun 13, 2015
3059fda
Updated 2.7/typing.py with covariant type vars for immutable sequences.
spkersten Jun 13, 2015
51d49a8
Fixed Mapping in typing.py(i) to be invariant in key type.
spkersten Jun 13, 2015
505c824
Renamed assert_proper_subtype to assert_strict_subtype to be more acc…
spkersten Jun 13, 2015
6a0ef53
Added *variance test cases for is_proper_subtype.
spkersten Jun 13, 2015
18a2b15
Made type variables invariant (the default) in TypeOpsSuite.
spkersten Jun 13, 2015
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
11 changes: 6 additions & 5 deletions lib-typing/2.7/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def NamedTuple(typename, fields):


class TypeVar(object):
def __init__(self, name, *values):
def __init__(self, name, *values, **kwargs):
self.name = name
if not values:
values = None
Expand Down Expand Up @@ -204,6 +204,7 @@ def overload(func):
T = TypeVar('T')
KT = TypeVar('KT')
VT = TypeVar('VT')
VT_co = TypeVar('VT_co', covariant=True)


class SupportsInt(_Protocol):
Expand Down Expand Up @@ -231,7 +232,7 @@ class Sized(_Protocol):
def __len__(self): pass


class Container(_Protocol[T]):
class Container(_Protocol[VT_co]):
@abstractmethod
def __contains__(self, x): pass

Expand All @@ -241,12 +242,12 @@ class Iterable(_Protocol[T]):
def __iter__(self): pass


class Iterator(Iterable[T], _Protocol[T]):
class Iterator(Iterable[VT_co], _Protocol[VT_co]):
@abstractmethod
def next(self): pass


class Sequence(Sized, Iterable[T], Container[T], Generic[T]):
class Sequence(Sized, Iterable[VT_co], Container[VT_co], Generic[VT_co]):
@abstractmethod
def __getitem__(self, i): pass

Expand All @@ -267,7 +268,7 @@ def count(self, x): pass
Sequence.register(t)


class AbstractSet(Sized, Iterable[T], Generic[T]):
class AbstractSet(Sized, Iterable[VT_co], Generic[VT_co]):
@abstractmethod
def __contains__(self, x): pass
@abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion lib-typing/3.2/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1374,7 +1374,7 @@ class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet):
pass


class Mapping(Sized, Iterable[KT_co], Container[KT_co], Generic[KT_co, VT_co],
class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co],
extra=collections_abc.Mapping):
pass

Expand Down
10 changes: 5 additions & 5 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ def add_class_tvars(t: Type, info: TypeInfo, is_classmethod: bool,
builtin_type: Callable[[str], Instance]) -> Type:
if isinstance(t, CallableType):
# TODO: Should we propagate type variable values?
vars = [TypeVarDef(n, i + 1, None, builtin_type('builtins.object'))
for i, n in enumerate(info.type_vars)]
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
Expand Down Expand Up @@ -290,7 +290,7 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance)
"""Create a type object type based on the signature of __init__."""
variables = [] # type: List[TypeVarDef]
for i, tvar in enumerate(info.defn.type_vars):
variables.append(TypeVarDef(tvar.name, i + 1, tvar.values, tvar.upper_bound))
variables.append(TypeVarDef(tvar.name, i + 1, tvar.values, tvar.upper_bound, tvar.variance))

initvars = init_type.variables
variables.extend(initvars)
Expand Down Expand Up @@ -319,7 +319,7 @@ def visit_type_var(self, t: TypeVarType) -> Type:
if t.id < 0:
return t
else:
return TypeVarType(t.name, -t.id - self.num_func_tvars, t.values, t.upper_bound)
return TypeVarType(t.name, -t.id - self.num_func_tvars, t.values, t.upper_bound, t.variance)

def translate_variables(self,
variables: List[TypeVarDef]) -> List[TypeVarDef]:
Expand All @@ -329,7 +329,7 @@ def translate_variables(self,
for v in variables:
if v.id > 0:
items.append(TypeVarDef(v.name, -v.id - self.num_func_tvars,
v.values, v.upper_bound))
v.values, v.upper_bound, v.variance))
else:
items.append(v)
return items
6 changes: 3 additions & 3 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
UnionType, FunctionLike
)
from mypy.maptype import map_instance_to_supertype
from mypy.subtypes import is_subtype, is_equivalent
from mypy.subtypes import is_subtype, is_equivalent, is_subtype_ignoring_tvars


def join_simple(declaration: Type, s: Type, t: Type) -> Type:
Expand Down Expand Up @@ -179,7 +179,7 @@ def join_instances(t: Instance, s: Instance) -> Type:
if t.type == s.type:
# Simplest case: join two types with the same base type (but
# potentially different arguments).
if is_subtype(t, s):
if is_subtype(t, s) or is_subtype(s, t):
# Compatible; combine type arguments.
args = [] # type: List[Type]
for i in range(len(t.args)):
Expand All @@ -188,7 +188,7 @@ def join_instances(t: Instance, s: Instance) -> Type:
else:
# Incompatible; return trivial result object.
return object_from_instance(t)
elif t.type.bases and is_subtype(t, s):
elif t.type.bases and is_subtype_ignoring_tvars(t, s):
return join_instances_via_supertype(t, s)
else:
# Now t is not a subtype of s, and t != s. Now s could be a subtype
Expand Down
4 changes: 2 additions & 2 deletions mypy/meet.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def meet_types(s: Type, t: Type) -> Type:
return t.accept(TypeMeetVisitor(s))


def meet_simple(s: Type, t: Type, default_right: bool = True) -> Type:
def meet_simple(s: Type, t: Type, default_right: bool=True) -> Type:
if s == t:
return s
if isinstance(s, UnionType):
Expand Down Expand Up @@ -131,7 +131,7 @@ def visit_instance(self, t: Instance) -> Type:
if isinstance(self.s, Instance):
si = cast(Instance, self.s)
if t.type == si.type:
if is_subtype(t, self.s):
if is_subtype(t, self.s) or is_subtype(self.s, t):
# Combine type arguments. We could have used join below
# equivalently.
args = [] # type: List[Type]
Expand Down
9 changes: 8 additions & 1 deletion mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,10 @@ def accept(self, visitor: NodeVisitor[T]) -> T:
return visitor.visit_type_application(self)


INVARIANT = 0 # type: int
COVARIANT = 1 # type: int
CONTRAVARIANT = 2 # type: int

class TypeVarExpr(SymbolNode):
"""Type variable expression TypeVar(...)."""

Expand All @@ -1300,12 +1304,15 @@ class TypeVarExpr(SymbolNode):
# Value restriction: only types in the list are valid as values. If the
# list is empty, there is no restriction.
values = None # type: List[mypy.types.Type]
variance = INVARIANT

def __init__(self, name: str, fullname: str,
values: List['mypy.types.Type']) -> None:
values: List['mypy.types.Type'],
variance: int=INVARIANT) -> None:
self._name = name
self._fullname = fullname
self.values = values
self.variance = variance

def name(self) -> str:
return self._name
Expand Down
Loading