diff --git a/mypy/checker.py b/mypy/checker.py index 760a137500f9..16a7e2d4e9cd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1624,7 +1624,7 @@ def append_types_for_inference(lvs: List[Expression], rv_types: List[Type]) -> N append_types_for_inference(right_lvs, right_rv_types) - return TupleType(type_parameters, self.named_type('builtins.tuple')) + return TupleType(type_parameters, self.named_generic_type('builtins.tuple', [AnyType()])) def split_around_star(self, items: List[T], star_index: int, length: int) -> Tuple[List[T], List[T], List[T]]: @@ -1689,7 +1689,7 @@ def check_lvalue(self, lvalue: Lvalue) -> Tuple[Optional[Type], self.store_type(lvalue, lvalue_type) elif isinstance(lvalue, TupleExpr) or isinstance(lvalue, ListExpr): types = [self.check_lvalue(sub_expr)[0] for sub_expr in lvalue.items] - lvalue_type = TupleType(types, self.named_type('builtins.tuple')) + lvalue_type = TupleType(types, self.named_generic_type('builtins.tuple', [AnyType()])) else: lvalue_type = self.expr_checker.accept(lvalue) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ebe1da07e942..f9b6066aa478 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1858,7 +1858,7 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: tt = self.accept(item, type_context_items[j]) j += 1 items.append(tt) - fallback_item = join.join_type_list(items) + fallback_item = UnionType.make_simplified_union(items) return TupleType(items, self.chk.named_generic_type('builtins.tuple', [fallback_item])) def visit_dict_expr(self, e: DictExpr) -> Type: @@ -1898,7 +1898,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: # # def (*v: Tuple[kt, vt]) -> Dict[kt, vt]: ... constructor = CallableType( - [TupleType([kt, vt], self.named_type('builtins.tuple'))], + [TupleType([kt, vt], self.chk.named_generic_type('builtins.tuple', [AnyType()]))], [nodes.ARG_STAR], [None], self.chk.named_generic_type('builtins.dict', [kt, vt]), @@ -2325,8 +2325,9 @@ def check_awaitable_expr(self, t: Type, ctx: Context, msg: str) -> Type: Also used by `async for` and `async with`. """ - if not self.chk.check_subtype(t, self.named_type('typing.Awaitable'), ctx, - msg, 'actual type', 'expected type'): + if not self.chk.check_subtype( + t, self.chk.named_generic_type('typing.Awaitable', [AnyType()]), ctx, msg, + 'actual type', 'expected type'): return AnyType() else: method = self.analyze_external_member_access('__await__', t, ctx) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 937ed8b73a04..8f1062ae9ce2 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -106,7 +106,9 @@ def visit_overloaded(self, t: Overloaded) -> Type: return Overloaded(items) def visit_tuple_type(self, t: TupleType) -> Type: - return t.copy_modified(items=self.expand_types(t.items)) + new_fallback = expand_type(t.fallback, self.variables) + assert isinstance(new_fallback, Instance) + return t.copy_modified(items=self.expand_types(t.items), fallback=new_fallback) def visit_typeddict_type(self, t: TypedDictType) -> Type: return t.copy_modified(item_types=self.expand_types(t.items.values())) diff --git a/mypy/messages.py b/mypy/messages.py index 44bc57548ada..529e3a88976a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -264,7 +264,10 @@ def format_simple(self, typ: Type, verbosity: int = 0) -> str: return '"{}"'.format(base_str) elif itype.type.fullname() == 'builtins.tuple': item_type_str = strip_quotes(self.format(itype.args[0])) - return 'Tuple[{}, ...]'.format(item_type_str) + if isinstance(itype.args[0], AnyType): + return 'tuple' + else: + return 'Tuple[{}, ...]'.format(item_type_str) elif itype.type.fullname() in reverse_type_aliases: alias = reverse_type_aliases[itype.type.fullname()] alias = alias.split('.')[-1] diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 589e9b812a3e..2cfeac3b2f75 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -142,10 +142,9 @@ def visit_instance(self, left: Instance) -> bool: rname = right.type.fullname() if not left.type.has_base(rname) and rname != 'builtins.object': return False - # Map left type to corresponding right instances. t = map_instance_to_supertype(left, right.type) - + # TODO: assert len(t.args) == len(right.args) return all(self.check_type_parameter(lefta, righta, tvar.variance) for lefta, righta, tvar in zip(t.args, right.args, right.type.defn.type_vars)) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index da3461795192..1d66834d4a7a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -260,38 +260,33 @@ def visit_unbound_type(self, t: UnboundType) -> Type: self.fail('Invalid type "{}"'.format(name), t) return t info = sym.node # type: TypeInfo - if len(t.args) > 0 and info.fullname() == 'builtins.tuple': - return TupleType(self.anal_array(t.args), - Instance(info, [AnyType()], t.line), - t.line) - else: - # Analyze arguments and construct Instance type. The - # number of type arguments and their values are - # checked only later, since we do not always know the - # valid count at this point. Thus we may construct an - # Instance with an invalid number of type arguments. - instance = Instance(info, self.anal_array(t.args), t.line, t.column) - instance.from_generic_builtin = sym.normalized - tup = info.tuple_type - if tup is not None: - # The class has a Tuple[...] base class so it will be - # represented as a tuple type. - if t.args: - self.fail('Generic tuple types not supported', t) - return AnyType() - return tup.copy_modified(items=self.anal_array(tup.items), - fallback=instance) - td = info.typeddict_type - if td is not None: - # The class has a TypedDict[...] base class so it will be - # represented as a typeddict type. - if t.args: - self.fail('Generic TypedDict types not supported', t) - return AnyType() - # Create a named TypedDictType - return td.copy_modified(item_types=self.anal_array(list(td.items.values())), - fallback=instance) - return instance + # Analyze arguments and construct Instance type. The + # number of type arguments and their values are + # checked only later, since we do not always know the + # valid count at this point. Thus we may construct an + # Instance with an invalid number of type arguments. + instance = Instance(info, self.anal_array(t.args), t.line, t.column) + instance.from_generic_builtin = sym.normalized + tup = info.tuple_type + if tup is not None: + # The class has a Tuple[...] base class so it will be + # represented as a tuple type. + if t.args: + self.fail('Generic tuple types not supported', t) + return AnyType() + return tup.copy_modified(items=self.anal_array(tup.items), + fallback=instance) + td = info.typeddict_type + if td is not None: + # The class has a TypedDict[...] base class so it will be + # represented as a typeddict type. + if t.args: + self.fail('Generic TypedDict types not supported', t) + return AnyType() + # Create a named TypedDictType + return td.copy_modified(item_types=self.anal_array(list(td.items.values())), + fallback=instance) + return instance else: return AnyType() @@ -345,7 +340,7 @@ def visit_tuple_type(self, t: TupleType) -> Type: self.fail('At most one star type allowed in a tuple', t) if t.implicit: return TupleType([AnyType() for _ in t.items], - self.named_type('builtins.tuple'), + self.named_type('builtins.tuple', [AnyType()]), t.line) else: return AnyType() diff --git a/mypy/types.py b/mypy/types.py index 0001be92d482..d933c42d3f19 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -425,6 +425,7 @@ class Instance(Type): def __init__(self, typ: mypy.nodes.TypeInfo, args: List[Type], line: int = -1, column: int = -1, erased: bool = False) -> None: + # TODO: assert(typ is NOT_READY or typ.fullname() != 'builtins.tuple' or len(args) == 1) assert(typ is NOT_READY or typ.fullname() not in ["builtins.Any", "typing.Any"]) self.type = typ self.args = args @@ -888,6 +889,8 @@ def __init__(self, items: List[Type], fallback: Instance, line: int = -1, column: int = -1, implicit: bool = False) -> None: self.items = items self.fallback = fallback + # TODO: assert not (isinstance(fallback, Instance) and fallback.type and + # fallback.type.fullname() == 'builtins.tuple' and not fallback.args) self.implicit = implicit self.can_be_true = len(self.items) > 0 self.can_be_false = len(self.items) == 0 @@ -922,7 +925,10 @@ def copy_modified(self, *, fallback: Optional[Instance] = None, return TupleType(items, fallback, self.line, self.column) def slice(self, begin: int, stride: int, end: int) -> 'TupleType': - return TupleType(self.items[begin:end:stride], self.fallback, + new_items = self.items[begin:end:stride] + fallback_args = [UnionType.make_simplified_union(new_items)] + new_fallback = self.fallback.copy_modified(args=fallback_args) + return TupleType(new_items, new_fallback, self.line, self.column, self.implicit) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index bc5a56b56014..5f8c71aa5fca 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1745,7 +1745,7 @@ f(1, thing_in_kwargs=["hey"]) from typing import Tuple, Any def f(x, *args): # type: (...) -> None success_tuple_type = args # type: Tuple[Any, ...] - fail_tuple_type = args # type: None # E: Incompatible types in assignment (expression has type Tuple[Any, ...], variable has type None) + fail_tuple_type = args # type: None # E: Incompatible types in assignment (expression has type tuple, variable has type None) f(1, "hello") [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 42cd312c0531..c58d81abec2c 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1789,6 +1789,8 @@ def g(x: Union[int, str]): pass c = a if f() else b g(c) # E: Argument 1 to "g" has incompatible type "Union[int, str, tuple]"; expected "Union[int, str]" +[builtins fixtures/list.pyi] + [case testUnificationMultipleInheritance] class A: pass class B: diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 7d313da71452..1f9eab890cae 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -415,7 +415,7 @@ b = B._make(['']) # type: B [case testNamedTupleIncompatibleRedefinition] from typing import NamedTuple class Crash(NamedTuple): - count: int # E: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as Callable[[Tuple[Any, ...], Any], int]) + count: int # E: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as Callable[[tuple, Any], int]) [builtins fixtures/tuple.pyi] [case testNamedTupleInClassNamespace] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index fe2180f16e29..b4d83ed8f104 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -91,7 +91,7 @@ from typing import Tuple t1 = None # type: Tuple[A, A] t2 = None # type: tuple -t1 = t2 # E: Incompatible types in assignment (expression has type Tuple[Any, ...], variable has type "Tuple[A, A]") +t1 = t2 # E: Incompatible types in assignment (expression has type tuple, variable has type "Tuple[A, A]") t2 = t1 class A: pass @@ -604,10 +604,10 @@ b = s in t [file builtins.py] from typing import TypeVar, Generic -_T = TypeVar('_T') +_T_co = TypeVar('_T_co', covariant=True) class object: def __init__(self) -> None: pass -class tuple(Generic[_T]): +class tuple(Generic[_T_co]): def __len__(self) -> int: pass def __str__(self) -> str: pass def __contains__(self, o: object) -> bool: pass @@ -702,6 +702,7 @@ B()[100] [case testValidTupleBaseClass] from typing import Tuple class A(tuple): pass +[builtins fixtures/tuple.pyi] [out] [case testTupleBaseClass2-skip] @@ -913,7 +914,7 @@ def f(a: Tuple) -> None: pass f(()) f((1,)) f(('', '')) -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected Tuple[Any, ...] +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected tuple [builtins fixtures/tuple.pyi] [case testTupleSingleton] diff --git a/test-data/unit/fixtures/async_await.pyi b/test-data/unit/fixtures/async_await.pyi index 42c1b53eb4bd..763e9fbc2f7e 100644 --- a/test-data/unit/fixtures/async_await.pyi +++ b/test-data/unit/fixtures/async_await.pyi @@ -1,7 +1,7 @@ import typing T = typing.TypeVar('T') -class list(typing.Generic[T], typing.Sequence[T]): pass +class list(typing.Sequence[T]): pass class object: def __init__(self): pass @@ -11,7 +11,7 @@ class int: pass class str: pass class dict: pass class set: pass -class tuple: pass +class tuple(typing.Generic[T]): pass class BaseException: pass class StopIteration(BaseException): pass class StopAsyncIteration(BaseException): pass diff --git a/test-data/unit/fixtures/bool.pyi b/test-data/unit/fixtures/bool.pyi index c4b4f3080f15..a1d1b9c1fdf5 100644 --- a/test-data/unit/fixtures/bool.pyi +++ b/test-data/unit/fixtures/bool.pyi @@ -1,10 +1,12 @@ # builtins stub used in boolean-related test cases. +from typing import Generic, TypeVar +T = TypeVar('T') class object: def __init__(self) -> None: pass class type: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class bool: pass class int: pass diff --git a/test-data/unit/fixtures/dict.pyi b/test-data/unit/fixtures/dict.pyi index 4182afb0eecd..a271315e4dde 100644 --- a/test-data/unit/fixtures/dict.pyi +++ b/test-data/unit/fixtures/dict.pyi @@ -35,7 +35,7 @@ class list(Iterable[T], Generic[T]): # needed by some test cases def __iter__(self) -> Iterator[T]: pass def __mul__(self, x: int) -> list[T]: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class float: pass class bool: pass diff --git a/test-data/unit/fixtures/exception.pyi b/test-data/unit/fixtures/exception.pyi index 5a2482dc1f87..999a73739364 100644 --- a/test-data/unit/fixtures/exception.pyi +++ b/test-data/unit/fixtures/exception.pyi @@ -1,9 +1,11 @@ +from typing import Generic, TypeVar +T = TypeVar('T') class object: def __init__(self): pass class type: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class int: pass class str: pass diff --git a/test-data/unit/fixtures/fine_grained.pyi b/test-data/unit/fixtures/fine_grained.pyi index 83429cd38314..b2e104ccfceb 100644 --- a/test-data/unit/fixtures/fine_grained.pyi +++ b/test-data/unit/fixtures/fine_grained.pyi @@ -4,6 +4,9 @@ # enough to handle them. import types +from typing import TypeVar, Generic + +T = TypeVar('T') class Any: pass @@ -20,7 +23,7 @@ class str: class float: pass class bytes: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class ellipsis: pass -class list: pass +class list(Generic[T]): pass diff --git a/test-data/unit/fixtures/float.pyi b/test-data/unit/fixtures/float.pyi index 38bdc08a03c5..1126d6158c6a 100644 --- a/test-data/unit/fixtures/float.pyi +++ b/test-data/unit/fixtures/float.pyi @@ -1,3 +1,6 @@ +from typing import Generic, TypeVar +T = TypeVar('T') + Any = 0 class object: @@ -12,7 +15,7 @@ class str: class bytes: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class ellipsis: pass diff --git a/test-data/unit/fixtures/floatdict.pyi b/test-data/unit/fixtures/floatdict.pyi index 9a34f8d369a0..54850d7f6e27 100644 --- a/test-data/unit/fixtures/floatdict.pyi +++ b/test-data/unit/fixtures/floatdict.pyi @@ -18,7 +18,7 @@ class str: class bytes: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class ellipsis: pass diff --git a/test-data/unit/fixtures/for.pyi b/test-data/unit/fixtures/for.pyi index 47628062f118..8b8ce1c3a500 100644 --- a/test-data/unit/fixtures/for.pyi +++ b/test-data/unit/fixtures/for.pyi @@ -9,7 +9,7 @@ class object: def __init__(self) -> None: pass class type: pass -class tuple: pass +class tuple(Generic[t]): pass class function: pass class bool: pass class int: pass # for convenience diff --git a/test-data/unit/fixtures/isinstancelist.pyi b/test-data/unit/fixtures/isinstancelist.pyi index 5ee49b86f7fd..dd1d90f9e547 100644 --- a/test-data/unit/fixtures/isinstancelist.pyi +++ b/test-data/unit/fixtures/isinstancelist.pyi @@ -1,4 +1,4 @@ -from typing import Iterable, Iterator, TypeVar, List, Mapping, overload, Tuple, Set, Union +from typing import Iterable, Iterator, TypeVar, List, Mapping, overload, Tuple, Set, Union, Sequence, Generic class object: def __init__(self) -> None: pass @@ -6,7 +6,6 @@ class object: class type: def __init__(self, x) -> None: pass -class tuple: pass class function: pass class ellipsis: pass @@ -24,6 +23,10 @@ T = TypeVar('T') KT = TypeVar('KT') VT = TypeVar('VT') +class tuple(Sequence[T], Generic[T]): + def __iter__(self) -> Iterator[T]: pass + def __getitem__(self, x: int) -> T: pass + class list(Iterable[T]): def __iter__(self) -> Iterator[T]: pass def __mul__(self, x: int) -> list[T]: pass diff --git a/test-data/unit/fixtures/list.pyi b/test-data/unit/fixtures/list.pyi index d5d1000d3364..c949a7644ea7 100644 --- a/test-data/unit/fixtures/list.pyi +++ b/test-data/unit/fixtures/list.pyi @@ -23,6 +23,7 @@ class list(Iterable[T], Generic[T]): def extend(self, x: Iterable[T]) -> None: pass class tuple(Generic[T]): pass + class function: pass class int: pass class float: pass diff --git a/test-data/unit/fixtures/module.pyi b/test-data/unit/fixtures/module.pyi index 44a4dfe0c277..ac1d3688ed12 100644 --- a/test-data/unit/fixtures/module.pyi +++ b/test-data/unit/fixtures/module.pyi @@ -13,7 +13,7 @@ class function: pass class int: pass class str: pass class bool: pass -class tuple: pass +class tuple(Generic[T]): pass class dict(Generic[T, S]): pass class ellipsis: pass diff --git a/test-data/unit/fixtures/module_all.pyi b/test-data/unit/fixtures/module_all.pyi index 2ab6bc66f6f0..87959fefbff5 100644 --- a/test-data/unit/fixtures/module_all.pyi +++ b/test-data/unit/fixtures/module_all.pyi @@ -14,5 +14,5 @@ class list(Generic[_T], Sequence[_T]): def append(self, x: _T): pass def extend(self, x: Sequence[_T]): pass def __add__(self, rhs: Sequence[_T]) -> list[_T]: pass -class tuple: pass +class tuple(Generic[_T]): pass class ellipsis: pass diff --git a/test-data/unit/fixtures/module_all_python2.pyi b/test-data/unit/fixtures/module_all_python2.pyi index 5a48e60c512d..989333c5f41a 100644 --- a/test-data/unit/fixtures/module_all_python2.pyi +++ b/test-data/unit/fixtures/module_all_python2.pyi @@ -12,4 +12,4 @@ class list(Generic[_T], Sequence[_T]): def append(self, x: _T): pass def extend(self, x: Sequence[_T]): pass def __add__(self, rhs: Sequence[_T]) -> list[_T]: pass -class tuple: pass +class tuple(Generic[_T]): pass diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index 4b5611b0bace..6cedba37c6c1 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -1,4 +1,6 @@ # builtins stub with non-generic primitive types +from typing import Generic, TypeVar +T = TypeVar('T') class object: def __init__(self) -> None: pass @@ -17,5 +19,5 @@ class str: def format(self, *args) -> str: pass class bytes: pass class bytearray: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass diff --git a/test-data/unit/fixtures/set.pyi b/test-data/unit/fixtures/set.pyi index cb8bbcf841e7..79d53e832291 100644 --- a/test-data/unit/fixtures/set.pyi +++ b/test-data/unit/fixtures/set.pyi @@ -8,7 +8,7 @@ class object: def __init__(self) -> None: pass class type: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class int: pass diff --git a/test-data/unit/fixtures/slice.pyi b/test-data/unit/fixtures/slice.pyi index c01ffbb1ca4c..47c1999aecb7 100644 --- a/test-data/unit/fixtures/slice.pyi +++ b/test-data/unit/fixtures/slice.pyi @@ -1,10 +1,12 @@ # Builtins stub used in slicing test cases. +from typing import Generic, TypeVar +T = TypeVar('T') class object: def __init__(self): pass class type: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class int: pass diff --git a/test-data/unit/fixtures/type.pyi b/test-data/unit/fixtures/type.pyi index 7600021a30ea..4a2dcac1ab4b 100644 --- a/test-data/unit/fixtures/type.pyi +++ b/test-data/unit/fixtures/type.pyi @@ -13,7 +13,7 @@ class list(Generic[T]): pass class type: def mro(self) -> List['type']: pass -class tuple: pass +class tuple(Generic[T]): pass class function: pass class bool: pass class int: pass diff --git a/test-data/unit/fixtures/union.pyi b/test-data/unit/fixtures/union.pyi index 78a41f9272e6..489e3ddb6ef9 100644 --- a/test-data/unit/fixtures/union.pyi +++ b/test-data/unit/fixtures/union.pyi @@ -1,7 +1,8 @@ # Builtins stub used in tuple-related test cases. from isinstance import isinstance -from typing import Iterable, TypeVar +from typing import Iterable, TypeVar, Generic +T = TypeVar('T') class object: def __init__(self): pass @@ -9,9 +10,7 @@ class object: class type: pass class function: pass -# Current tuple types get special treatment in the type checker, thus there -# is no need for type arguments here. -class tuple: pass +class tuple(Generic[T]): pass # We need int for indexing tuples. class int: pass