Skip to content

Commit 3003e50

Browse files
committed
Compute type of inherited constructor correctly
Fixes #1246.
1 parent 2db9dc3 commit 3003e50

File tree

3 files changed

+79
-30
lines changed

3 files changed

+79
-30
lines changed

mypy/checker.py

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from mypy.sametypes import is_same_type
3535
from mypy.messages import MessageBuilder
3636
import mypy.checkexpr
37+
from mypy.checkmember import map_type_from_supertype
3738
from mypy import defaults
3839
from mypy import messages
3940
from mypy.subtypes import (
@@ -2326,36 +2327,6 @@ def method_type(self, func: FuncBase) -> FunctionLike:
23262327
return method_type_with_fallback(func, self.named_type('builtins.function'))
23272328

23282329

2329-
def map_type_from_supertype(typ: Type, sub_info: TypeInfo,
2330-
super_info: TypeInfo) -> Type:
2331-
"""Map type variables in a type defined in a supertype context to be valid
2332-
in the subtype context. Assume that the result is unique; if more than
2333-
one type is possible, return one of the alternatives.
2334-
2335-
For example, assume
2336-
2337-
. class D(Generic[S]) ...
2338-
. class C(D[E[T]], Generic[T]) ...
2339-
2340-
Now S in the context of D would be mapped to E[T] in the context of C.
2341-
"""
2342-
# Create the type of self in subtype, of form t[a1, ...].
2343-
inst_type = self_type(sub_info)
2344-
if isinstance(inst_type, TupleType):
2345-
inst_type = inst_type.fallback
2346-
# Map the type of self to supertype. This gets us a description of the
2347-
# supertype type variables in terms of subtype variables, i.e. t[t1, ...]
2348-
# so that any type variables in tN are to be interpreted in subtype
2349-
# context.
2350-
inst_type = map_instance_to_supertype(inst_type, super_info)
2351-
# Finally expand the type variables in type with those in the previously
2352-
# constructed type. Note that both type and inst_type may have type
2353-
# variables, but in type they are interpreterd in supertype context while
2354-
# in inst_type they are interpreted in subtype context. This works even if
2355-
# the names of type variables in supertype and subtype overlap.
2356-
return expand_type_by_instance(typ, inst_type)
2357-
2358-
23592330
def find_isinstance_check(node: Node,
23602331
type_map: Dict[Node, Type],
23612332
weak: bool=False) \

mypy/checkmember.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,19 @@ def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) ->
359359
def type_object_type_from_function(init_or_new: FuncBase, info: TypeInfo,
360360
fallback: Instance) -> FunctionLike:
361361
signature = method_type_with_fallback(init_or_new, fallback)
362+
363+
# The __init__ method might come from a generic superclass
364+
# (init_or_new.info) with type variables that do not map
365+
# identically to the type variables of the class being constructed
366+
# (info). For example
367+
#
368+
# class A(Generic[T]): def __init__(self, x: T) -> None: pass
369+
# class B(A[List[T]], Generic[T]): pass
370+
#
371+
# We need to first map B's __init__ to the type (List[T]) -> None.
372+
signature = cast(FunctionLike,
373+
map_type_from_supertype(signature, info, init_or_new.info))
374+
362375
if isinstance(signature, CallableType):
363376
return class_callable(signature, info, fallback)
364377
else:
@@ -416,3 +429,33 @@ def translate_variables(self,
416429
else:
417430
items.append(v)
418431
return items
432+
433+
434+
def map_type_from_supertype(typ: Type, sub_info: TypeInfo,
435+
super_info: TypeInfo) -> Type:
436+
"""Map type variables in a type defined in a supertype context to be valid
437+
in the subtype context. Assume that the result is unique; if more than
438+
one type is possible, return one of the alternatives.
439+
440+
For example, assume
441+
442+
. class D(Generic[S]) ...
443+
. class C(D[E[T]], Generic[T]) ...
444+
445+
Now S in the context of D would be mapped to E[T] in the context of C.
446+
"""
447+
# Create the type of self in subtype, of form t[a1, ...].
448+
inst_type = self_type(sub_info)
449+
if isinstance(inst_type, TupleType):
450+
inst_type = inst_type.fallback
451+
# Map the type of self to supertype. This gets us a description of the
452+
# supertype type variables in terms of subtype variables, i.e. t[t1, ...]
453+
# so that any type variables in tN are to be interpreted in subtype
454+
# context.
455+
inst_type = map_instance_to_supertype(inst_type, super_info)
456+
# Finally expand the type variables in type with those in the previously
457+
# constructed type. Note that both type and inst_type may have type
458+
# variables, but in type they are interpreterd in supertype context while
459+
# in inst_type they are interpreted in subtype context. This works even if
460+
# the names of type variables in supertype and subtype overlap.
461+
return expand_type_by_instance(typ, inst_type)

mypy/test/data/check-generic-subtyping.test

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,41 @@ class A(B[S], Generic[T, S]):
352352
main: note: In member "g" of class "A":
353353

354354

355+
-- Type of inherited constructor
356+
-- -----------------------------
357+
358+
359+
[case testInheritedConstructor]
360+
from typing import TypeVar, Generic
361+
T = TypeVar('T')
362+
class A(Generic[T]):
363+
def __init__(self, x: T) -> None: pass
364+
class B(A[T], Generic[T]): pass
365+
class C(A[int]): pass
366+
class D(A[A[T]], Generic[T]): pass
367+
B(1)
368+
C(1)
369+
C('a') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
370+
D(A(1))
371+
D(1) # E: Argument 1 to "D" has incompatible type "int"; expected A[None]
372+
373+
374+
[case testInheritedConstructor2]
375+
from typing import TypeVar, Generic
376+
T = TypeVar('T')
377+
U = TypeVar('U')
378+
Z = TypeVar('Z')
379+
class A(Generic[T, U]):
380+
def __init__(self, x: T, y: U, z: Z) -> None: pass
381+
class B(A[int, T], Generic[T]): pass
382+
class C(B[A[T, str]], Generic[T, U]): pass
383+
# C[T, U] <: B[A[T, str]] <: A[int, A[T, str]]
384+
C(1, A(1, 'a', 0), 'z')
385+
C(1, A('1', 'a', 0), 'z')
386+
C('1', A(1, 'a', 0), 'z') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
387+
C(1, A(1, 1, 0), 'z') # E: Argument 2 to "A" has incompatible type "int"; expected "str"
388+
389+
355390
-- Subtyping with a generic abstract base class
356391
-- --------------------------------------------
357392

0 commit comments

Comments
 (0)