Skip to content

Commit 041e4db

Browse files
committed
[2/2] Some enforcing of (co/contra)variance.
Reject covariant type variables being used as an argument type. Reject contravariant type variables being used as a return type. Fixes #734.
1 parent a98cc74 commit 041e4db

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

mypy/checker.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
LITERAL_TYPE, BreakStmt, ContinueStmt, ComparisonExpr, StarExpr,
2020
YieldFromExpr, YieldFromStmt, NamedTupleExpr, SetComprehension,
2121
DictionaryComprehension, ComplexExpr, EllipsisExpr, TypeAliasExpr,
22-
RefExpr, YieldExpr
22+
RefExpr, YieldExpr, CONTRAVARIANT, COVARIANT
2323
)
2424
from mypy.nodes import function_type, method_type, method_type_with_fallback
2525
from mypy import nodes
2626
from mypy.types import (
2727
Type, AnyType, CallableType, Void, FunctionLike, Overloaded, TupleType,
28-
Instance, NoneTyp, UnboundType, ErrorType, TypeTranslator, strip_type, UnionType
28+
Instance, NoneTyp, UnboundType, ErrorType, TypeTranslator, strip_type,
29+
UnionType, TypeVarType,
2930
)
3031
from mypy.sametypes import is_same_type
3132
from mypy.messages import MessageBuilder
@@ -504,12 +505,25 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: str) -> None:
504505
elif name == '__getattr__':
505506
self.check_getattr_method(typ, defn)
506507

508+
# Refuse contravariant return type variable
509+
if isinstance(typ.ret_type, TypeVarType):
510+
if typ.ret_type.variance == CONTRAVARIANT:
511+
self.fail(messages.RETURN_TYPE_CANNOT_BE_CONTRAVARIANT,
512+
typ.ret_type)
513+
507514
# Push return type.
508515
self.return_types.append(typ.ret_type)
509516

510517
# Store argument types.
511518
for i in range(len(typ.arg_types)):
512519
arg_type = typ.arg_types[i]
520+
521+
# Refuse covariant parameter type variables
522+
if isinstance(arg_type, TypeVarType):
523+
if arg_type.variance == COVARIANT:
524+
self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT,
525+
arg_type)
526+
513527
if typ.arg_kinds[i] == nodes.ARG_STAR:
514528
# builtins.tuple[T] is typing.Tuple[T, ...]
515529
arg_type = self.named_generic_type('builtins.tuple',

mypy/messages.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
FORMAT_REQUIRES_MAPPING = 'Format requires a mapping'
6767
GENERIC_TYPE_NOT_VALID_AS_EXPRESSION = \
6868
"Generic type not valid as an expression any more (use '# type:' comment instead)"
69+
RETURN_TYPE_CANNOT_BE_CONTRAVARIANT = "Cannot use a contravariant type variable as return type"
70+
FUNCTION_PARAMETER_CANNOT_BE_COVARIANT = "Cannot use a covariant type variable as a parameter"
6971

7072

7173
class MessageBuilder:

mypy/test/data/check-functions.test

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,3 +902,43 @@ def f():
902902
"""
903903
return 1
904904
f() + ''
905+
[case testRejectCovariantArgument]
906+
from typing import TypeVar, Generic
907+
908+
t = TypeVar('t', covariant=True)
909+
class A(Generic[t]):
910+
def foo(self, x: t) -> None:
911+
return None
912+
[builtins fixtures/bool.py]
913+
[out]
914+
main: note: In member "foo" of class "A":
915+
main:5: error: Cannot use a covariant type variable as a parameter
916+
917+
[case testRejectContravariantReturnType]
918+
from typing import TypeVar, Generic
919+
920+
t = TypeVar('t', contravariant=True)
921+
class A(Generic[t]):
922+
def foo(self) -> t:
923+
return None
924+
[builtins fixtures/bool.py]
925+
[out]
926+
main: note: In member "foo" of class "A":
927+
main:5: error: Cannot use a contravariant type variable as return type
928+
929+
[case testAcceptCovariantReturnType]
930+
from typing import TypeVar, Generic
931+
932+
t = TypeVar('t', covariant=True)
933+
class A(Generic[t]):
934+
def foo(self) -> t:
935+
return None
936+
[builtins fixtures/bool.py]
937+
[case testAcceptContravariantArgument]
938+
from typing import TypeVar, Generic
939+
940+
t = TypeVar('t', contravariant=True)
941+
class A(Generic[t]):
942+
def foo(self, x: t) -> None:
943+
return None
944+
[builtins fixtures/bool.py]

0 commit comments

Comments
 (0)