diff --git a/mypy/checker.py b/mypy/checker.py index 119aa9f3cea2..bf739e7d1242 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -125,6 +125,7 @@ TryStmt, TupleExpr, TypeAlias, + TypeAliasStmt, TypeInfo, TypeVarExpr, UnaryExpr, @@ -5289,6 +5290,9 @@ def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, if node not in inferred_types or not is_subtype(typ, inferred_types[node]): del type_map[expr] + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + self.expr_checker.accept(o.value) + def make_fake_typeinfo( self, curr_module_fullname: str, diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 861c28e5b54c..4fd1a308e560 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -411,7 +411,9 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result = self.alias_type_in_runtime_context( node, ctx=e, alias_definition=e.is_alias_rvalue or lvalue ) - elif isinstance(node, (TypeVarExpr, ParamSpecExpr, TypeVarTupleExpr)): + elif isinstance(node, TypeVarExpr): + return self.named_type("typing.TypeVar") + elif isinstance(node, (ParamSpecExpr, TypeVarTupleExpr)): result = self.object_type() else: if isinstance(node, PlaceholderNode): diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 70afe9010583..342cf36d69e8 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1791,7 +1791,13 @@ def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: type_params = self.translate_type_params(n.type_params) value = self.visit(n.value) - node = TypeAliasStmt(self.visit_Name(n.name), type_params, value) + # Since the value is evaluated lazily, wrap the value inside a lambda. + # This helps mypyc. + ret = ReturnStmt(value) + self.set_line(ret, n.value) + value_func = LambdaExpr(body=Block([ret])) + self.set_line(value_func, n.value) + node = TypeAliasStmt(self.visit_Name(n.name), type_params, value_func) return self.set_line(node, n) else: self.fail( diff --git a/mypy/nodes.py b/mypy/nodes.py index 850b1db87556..5d3a1d31aece 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1653,10 +1653,10 @@ class TypeAliasStmt(Statement): name: NameExpr type_args: list[TypeParam] - value: Expression # Will get translated into a type + value: LambdaExpr # Return value will get translated into a type invalid_recursive_alias: bool - def __init__(self, name: NameExpr, type_args: list[TypeParam], value: Expression) -> None: + def __init__(self, name: NameExpr, type_args: list[TypeParam], value: LambdaExpr) -> None: super().__init__() self.name = name self.type_args = type_args diff --git a/mypy/semanal.py b/mypy/semanal.py index d2f02d4835e2..03e6172bb325 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3766,6 +3766,10 @@ def analyze_alias( last_tvar_name_with_default = tvar_def.name tvar_defs.append(tvar_def) + if python_3_12_type_alias: + with self.allow_unbound_tvars_set(): + rvalue.accept(self) + analyzed, depends_on = analyze_type_alias( typ, self, @@ -5360,7 +5364,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: tag = self.track_incomplete_refs() res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( s.name.name, - s.value, + s.value.expr(), allow_placeholder=True, declared_type_vars=type_params, all_declared_type_params_names=all_type_params_names, @@ -5443,6 +5447,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: current_node = existing.node if existing else alias_node assert isinstance(current_node, TypeAlias) self.disable_invalid_recursive_aliases(s, current_node, s.value) + s.name.accept(self) finally: self.pop_type_args(s.type_args) @@ -5457,7 +5462,11 @@ def visit_name_expr(self, expr: NameExpr) -> None: def bind_name_expr(self, expr: NameExpr, sym: SymbolTableNode) -> None: """Bind name expression to a symbol table node.""" - if isinstance(sym.node, TypeVarExpr) and self.tvar_scope.get_binding(sym): + if ( + isinstance(sym.node, TypeVarExpr) + and self.tvar_scope.get_binding(sym) + and not self.allow_unbound_tvars + ): self.fail(f'"{expr.name}" is a type variable and only valid in type context', expr) elif isinstance(sym.node, PlaceholderNode): self.process_placeholder(expr.name, "name", expr) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 1b4f551d4a2a..a9e1ce471953 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -24,6 +24,9 @@ ARG_POS, GDEF, LDEF, + PARAM_SPEC_KIND, + TYPE_VAR_KIND, + TYPE_VAR_TUPLE_KIND, ArgKind, CallExpr, Decorator, @@ -44,6 +47,7 @@ TupleExpr, TypeAlias, TypeInfo, + TypeParam, UnaryExpr, Var, ) @@ -1409,3 +1413,45 @@ def get_call_target_fullname(ref: RefExpr) -> str: if isinstance(target, Instance): return target.type.fullname return ref.fullname + + +def create_type_params( + builder: IRBuilder, typing_mod: Value, type_args: list[TypeParam], line: int +) -> list[Value]: + """Create objects representing various kinds of Python 3.12 type parameters. + + The "typing_mod" argument is the "_typing" module object. The type objects + are looked up from it. + + The returned list has one item for each "type_args" item, in the same order. + Each item is either a TypeVar, TypeVarTuple or ParamSpec instance. + """ + tvs = [] + type_var_imported: Value | None = None + for type_param in type_args: + if type_param.kind == TYPE_VAR_KIND: + if type_var_imported: + # Reuse previously imported value as a minor optimization + tvt = type_var_imported + else: + tvt = builder.py_get_attr(typing_mod, "TypeVar", line) + type_var_imported = tvt + elif type_param.kind == TYPE_VAR_TUPLE_KIND: + tvt = builder.py_get_attr(typing_mod, "TypeVarTuple", line) + else: + assert type_param.kind == PARAM_SPEC_KIND + tvt = builder.py_get_attr(typing_mod, "ParamSpec", line) + if type_param.kind != TYPE_VAR_TUPLE_KIND: + # To match runtime semantics, pass infer_variance=True + tv = builder.py_call( + tvt, + [builder.load_str(type_param.name), builder.true()], + line, + arg_kinds=[ARG_POS, ARG_NAMED], + arg_names=[None, "infer_variance"], + ) + else: + tv = builder.py_call(tvt, [builder.load_str(type_param.name)], line) + builder.init_type_var(tv, type_param.name, line) + tvs.append(tv) + return tvs diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 303ee8849244..2152da099e81 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -7,8 +7,6 @@ from typing import Callable, Final from mypy.nodes import ( - PARAM_SPEC_KIND, - TYPE_VAR_KIND, TYPE_VAR_TUPLE_KIND, AssignmentStmt, CallExpr, @@ -57,7 +55,7 @@ is_optional_type, object_rprimitive, ) -from mypyc.irbuild.builder import IRBuilder +from mypyc.irbuild.builder import IRBuilder, create_type_params from mypyc.irbuild.function import ( gen_property_getter_ir, gen_property_setter_ir, @@ -475,35 +473,20 @@ def make_generic_base_class( ) -> Value: """Construct Generic[...] base class object for a new-style generic class (Python 3.12).""" mod = builder.call_c(import_op, [builder.load_str("_typing")], line) - tvs = [] - type_var_imported: Value | None = None - for type_param in type_args: - unpack = False - if type_param.kind == TYPE_VAR_KIND: - if type_var_imported: - # Reuse previously imported value as a minor optimization - tvt = type_var_imported - else: - tvt = builder.py_get_attr(mod, "TypeVar", line) - type_var_imported = tvt - elif type_param.kind == TYPE_VAR_TUPLE_KIND: - tvt = builder.py_get_attr(mod, "TypeVarTuple", line) - unpack = True - else: - assert type_param.kind == PARAM_SPEC_KIND - tvt = builder.py_get_attr(mod, "ParamSpec", line) - tv = builder.py_call(tvt, [builder.load_str(type_param.name)], line) - builder.init_type_var(tv, type_param.name, line) - if unpack: + tvs = create_type_params(builder, mod, type_args, line) + args = [] + for tv, type_param in zip(tvs, type_args): + if type_param.kind == TYPE_VAR_TUPLE_KIND: # Evaluate *Ts for a TypeVarTuple it = builder.call_c(iter_op, [tv], line) tv = builder.call_c(next_op, [it], line) - tvs.append(tv) + args.append(tv) + gent = builder.py_get_attr(mod, "Generic", line) - if len(tvs) == 1: - arg = tvs[0] + if len(args) == 1: + arg = args[0] else: - arg = builder.new_tuple(tvs, line) + arg = builder.new_tuple(args, line) base = builder.call_c(py_get_item_op, [gent, arg], line) return base diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 2c17eb2bb14d..4d828b1b9d82 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -12,6 +12,8 @@ from typing import Callable, Sequence from mypy.nodes import ( + ARG_NAMED, + ARG_POS, AssertStmt, AssignmentStmt, AwaitExpr, @@ -37,6 +39,7 @@ TempNode, TryStmt, TupleExpr, + TypeAliasStmt, WhileStmt, WithStmt, YieldExpr, @@ -74,7 +77,7 @@ object_rprimitive, ) from mypyc.irbuild.ast_helpers import is_borrow_friendly_expr, process_conditional -from mypyc.irbuild.builder import IRBuilder, int_borrow_friendly_op +from mypyc.irbuild.builder import IRBuilder, create_type_params, int_borrow_friendly_op from mypyc.irbuild.for_helpers import for_loop_helper from mypyc.irbuild.generator import add_raise_exception_blocks_to_generator_class from mypyc.irbuild.nonlocalcontrol import ( @@ -105,7 +108,9 @@ coro_op, import_from_many_op, import_many_op, + import_op, send_op, + set_type_alias_compute_function_op, type_op, yield_from_except_op, ) @@ -1015,3 +1020,30 @@ def transform_await_expr(builder: IRBuilder, o: AwaitExpr) -> Value: def transform_match_stmt(builder: IRBuilder, m: MatchStmt) -> None: m.accept(MatchVisitor(builder, m)) + + +def transform_type_alias_stmt(builder: IRBuilder, s: TypeAliasStmt) -> None: + line = s.line + # Use "_typing" to avoid importing "typing", as the latter can be expensive. + # "_typing" includes everything we need here. + mod = builder.call_c(import_op, [builder.load_str("_typing")], line) + type_params = create_type_params(builder, mod, s.type_args, s.line) + + type_alias_type = builder.py_get_attr(mod, "TypeAliasType", line) + args = [builder.load_str(s.name.name), builder.none()] + arg_names: list[str | None] = [None, None] + arg_kinds = [ARG_POS, ARG_POS] + if s.type_args: + args.append(builder.new_tuple(type_params, line)) + arg_names.append("type_params") + arg_kinds.append(ARG_NAMED) + alias = builder.py_call(type_alias_type, args, line, arg_names=arg_names, arg_kinds=arg_kinds) + + # Use primitive to set function used to lazily compute type alias type value. + # The value needs to be lazily computed to match Python runtime behavior, but + # Python public APIs don't support this, so we use a C primitive. + compute_fn = s.value.accept(builder.visitor) + builder.builder.primitive_op(set_type_alias_compute_function_op, [alias, compute_fn], line) + + target = builder.get_assignment_target(s.name) + builder.assign(target, alias, line) diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index e7256f036e4c..05a033c3e6ad 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -137,6 +137,7 @@ transform_raise_stmt, transform_return_stmt, transform_try_stmt, + transform_type_alias_stmt, transform_while_stmt, transform_with_stmt, transform_yield_expr, @@ -251,7 +252,7 @@ def visit_match_stmt(self, stmt: MatchStmt) -> None: transform_match_stmt(self.builder, stmt) def visit_type_alias_stmt(self, stmt: TypeAliasStmt) -> None: - self.bail('The "type" statement is not yet supported by mypyc', stmt.line) + transform_type_alias_stmt(self.builder, stmt) # Expressions diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 8aa5a77c180c..2ec04e4c5b5c 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -901,6 +901,7 @@ PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func, PyOb PyObject *CPy_GetAIter(PyObject *obj); PyObject *CPy_GetANext(PyObject *aiter); +void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value); #ifdef __cplusplus } diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index f28eeb57e646..803123d436a2 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -940,3 +940,34 @@ PyObject *CPy_GetANext(PyObject *aiter) error: return NULL; } + +#ifdef CPY_3_12_FEATURES + +// Copied from Python 3.12.3, since this struct is internal to CPython. It defines +// the structure of typing.TypeAliasType objects. We need it since compute_value is +// not part of the public API, and we need to set it to match Python runtime semantics. +// +// IMPORTANT: This needs to be kept in sync with CPython! +typedef struct { + PyObject_HEAD + PyObject *name; + PyObject *type_params; + PyObject *compute_value; + PyObject *value; + PyObject *module; +} typealiasobject; + +void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value) { + typealiasobject *obj = (typealiasobject *)alias; + if (obj->value != NULL) { + Py_DECREF(obj->value); + } + obj->value = NULL; + Py_INCREF(compute_value); + if (obj->compute_value != NULL) { + Py_DECREF(obj->compute_value); + } + obj->compute_value = compute_value; +} + +#endif diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index fea62bbb19c4..e9016e24c46d 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -265,3 +265,15 @@ return_type=c_pyssize_t_rprimitive, error_kind=ERR_NEVER, ) + +# Set the lazy value compute function of an TypeAliasType instance (Python 3.12+). +# This must only be used as part of initializing the object. Any existing value +# will be cleared. +set_type_alias_compute_function_op = custom_primitive_op( + name="set_type_alias_compute_function", + c_function_name="CPy_SetTypeAliasTypeComputeFunction", + # (alias object, value compute function) + arg_types=[object_rprimitive, object_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, +) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 6f0d8da90d57..ac95ffe2c047 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -45,6 +45,7 @@ def __ne__(self, x: object) -> bool: pass class type: def __init__(self, o: object) -> None: ... + def __or__(self, o: object) -> Any: ... __name__ : str __annotations__: Dict[str, Any] diff --git a/mypyc/test-data/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi index 3ddc1f1bba08..8bb3b1398f87 100644 --- a/mypyc/test-data/fixtures/typing-full.pyi +++ b/mypyc/test-data/fixtures/typing-full.pyi @@ -10,6 +10,9 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass +class _SpecialForm: + def __getitem__(self, index): ... + cast = 0 overload = 0 Any = 0 @@ -19,7 +22,6 @@ TypeVar = 0 Generic = 0 Protocol = 0 Tuple = 0 -Callable = 0 _promote = 0 NamedTuple = 0 Type = 0 @@ -30,6 +32,7 @@ Literal = 0 TypedDict = 0 NoReturn = 0 NewType = 0 +Callable: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/mypyc/test-data/run-python312.test b/mypyc/test-data/run-python312.test index fbafeaf3e65f..5e8a388fd8d3 100644 --- a/mypyc/test-data/run-python312.test +++ b/mypyc/test-data/run-python312.test @@ -169,4 +169,57 @@ def test_class_with_value_restriction() -> None: assert r.x == 1 r2 = Restriction[str]('a') assert r2.x == 'a' + +type A = int + +def test_simple_type_alias() -> None: + assert isinstance(A, TypeAliasType) + assert getattr(A, "__value__") is int + assert str(A) == "A" + +type B = Fwd[int] +Fwd = list + +def test_forward_reference_in_alias() -> None: + assert isinstance(B, TypeAliasType) + assert getattr(B, "__value__") == list[int] + +type R = int | list[R] + +def test_recursive_type_alias() -> None: + assert isinstance(R, TypeAliasType) + assert getattr(R, "__value__") == (int | list[R]) +[typing fixtures/typing-full.pyi] + +[case testPEP695GenericTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Callable +from types import GenericAlias + +from testutil import assertRaises + +type A[T] = list[T] + +def test_generic_alias() -> None: + assert type(A[str]) is GenericAlias + assert str(A[str]) == "A[str]" + assert str(getattr(A, "__value__")) == "list[T]" + +type B[T, S] = dict[S, T] + +def test_generic_alias_with_two_args() -> None: + assert str(B[str, int]) == "B[str, int]" + assert str(getattr(B, "__value__")) == "dict[S, T]" + +type C[*Ts] = tuple[*Ts] + +def test_type_var_tuple_type_alias() -> None: + assert str(C[int, str]) == "C[int, str]" + assert str(getattr(C, "__value__")) == "tuple[typing.Unpack[Ts]]" + +type D[**P] = Callable[P, int] + +def test_param_spec_type_alias() -> None: + assert str(D[[int, str]]) == "D[[int, str]]" + assert str(getattr(D, "__value__")) == "typing.Callable[P, int]" [typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index f334b9011645..fd564c7e96cb 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -535,7 +535,7 @@ class Base(NamedTuple): self.x = 3 # E: Property "x" defined in "Base" is read-only self[1] # E: Tuple index out of range reveal_type(self[T]) # N: Revealed type is "builtins.int" \ - # E: No overload variant of "__getitem__" of "tuple" matches argument type "object" \ + # E: No overload variant of "__getitem__" of "tuple" matches argument type "TypeVar" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> int \ # N: def __getitem__(self, slice, /) -> Tuple[int, ...] @@ -568,6 +568,7 @@ reveal_type(Base(1).bad_override()) # N: Revealed type is "builtins.int" reveal_type(takes_base(Base(1))) # N: Revealed type is "builtins.int" reveal_type(takes_base(Child(1))) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testNewNamedTupleIllegalNames] from typing import Callable, NamedTuple diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 04b3f7a131cc..4fc6e9a75c83 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2449,5 +2449,7 @@ def f() -> int: # E: Missing return statement from typing import TypeVar T = TypeVar("T") x: int -x + T # E: Unsupported operand types for + ("int" and "object") -T() # E: "object" not callable +x + T # E: Unsupported left operand type for + ("int") +T() # E: "TypeVar" not callable +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index ea3f501fd949..abcb2a4bbc48 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -624,6 +624,7 @@ reveal_type(y) X = T # Error [builtins fixtures/list.pyi] +[typing fixtures/typing-full.pyi] [out] main:9:5: error: "Node" expects 2 type arguments, but 1 given main:11:5: error: "Node" expects 2 type arguments, but 3 given diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 7cbed5637c3a..47e508ee1a6b 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2184,8 +2184,7 @@ from typing import TypeVar, Generic, Any T = TypeVar('T', bound='B[Any]') # The "int" error is because of typing fixture. T = TypeVar('T', bound='C') # E: Cannot redefine "T" as a type variable \ - # E: Invalid assignment target \ - # E: "int" not callable + # E: Invalid assignment target class B(Generic[T]): x: T @@ -2194,6 +2193,8 @@ class C: ... x: B[int] # E: Type argument "int" of "B" must be a subtype of "B[Any]" y: B[B[Any]] reveal_type(y.x) # N: Revealed type is "__main__.B[Any]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testNewAnalyzerDuplicateTypeVarImportCycle] # flags: --disable-error-code used-before-def @@ -2216,12 +2217,13 @@ class C: ... x: B[int] y: B[B[Any]] reveal_type(y.x) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [out] tmp/b.py:8: error: Type argument "int" of "B" must be a subtype of "B[Any]" tmp/b.py:10: note: Revealed type is "b.B[Any]" tmp/a.py:5: error: Cannot redefine "T" as a type variable tmp/a.py:5: error: Invalid assignment target -tmp/a.py:5: error: "int" not callable [case testNewAnalyzerDuplicateTypeVarImportCycleWithAliases] # flags: --disable-error-code used-before-def diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 06c5bada1e92..b3a3645dc9f8 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -41,7 +41,8 @@ reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, Any]" must be used \ [case test695TypeVar] from typing import Callable -type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported +type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported \ + # E: Name "T" is not defined type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported \ # E: Value of type "int" is not indexable \ # E: Name "P" is not defined @@ -52,7 +53,9 @@ class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported class Cls2[**P]: ... # E: PEP 695 generics are not yet supported class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported -def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported +def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported \ + # E: Name "T" is not defined + def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported \ # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas \ @@ -504,6 +507,7 @@ reveal_type(a3) # N: Revealed type is "__main__.D[builtins.str, __main__.C[buil type A4 = int | str a4: A4 reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/type.pyi] [case testPEP695TypeAliasWithUnusedTypeParams] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -531,6 +535,8 @@ a: A reveal_type(a) # N: Revealed type is "__main__.C" class C: pass +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695TypeAliasForwardReference3] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -579,12 +585,15 @@ reveal_type(a) # N: Revealed type is "Any" [case testPEP695TypeAliasInvalidType] # flags: --enable-incomplete-feature=NewGenericSyntax -type A = int | 1 # E: Invalid type: try using Literal[1] instead? +type A = int | 1 # E: Invalid type: try using Literal[1] instead? \ + # E: Unsupported operand types for | ("Type[int]" and "int") + a: A reveal_type(a) # N: Revealed type is "Union[builtins.int, Any]" type B = int + str # E: Invalid type alias: expression is not a valid type b: B reveal_type(b) # N: Revealed type is "Any" +[builtins fixtures/type.pyi] [case testPEP695TypeAliasBoundForwardReference] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -809,6 +818,7 @@ type C[**P] = Callable[P, int] f: C[[str, int | None]] reveal_type(f) # N: Revealed type is "def (builtins.str, Union[builtins.int, None]) -> builtins.int" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695TypeVarTuple] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -1062,7 +1072,7 @@ from typing import cast def f[T]( x: T = T # E: Name "T" is not defined \ - # E: Incompatible default for argument "x" (default has type "object", argument has type "T") + # E: Incompatible default for argument "x" (default has type "TypeVar", argument has type "T") ) -> T: return x @@ -1072,6 +1082,8 @@ def g[T](x: T = cast(T, None)) -> T: # E: Name "T" is not defined class C: def m[T](self, x: T = cast(T, None)) -> T: # E: Name "T" is not defined return x +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695ListComprehension] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1174,6 +1186,7 @@ class C[T]: pass type B[T] = C[T] | list[B[T]] b: B[int] reveal_type(b) # N: Revealed type is "Union[__main__.C[builtins.int], builtins.list[...]]" +[builtins fixtures/type.pyi] [case testPEP695BadRecursiveTypeAlias] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1184,6 +1197,8 @@ a: A reveal_type(a) # N: Revealed type is "Any" b: B reveal_type(b) # N: Revealed type is "Any" +[builtins fixtures/type.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695RecursiveTypeAliasForwardReference] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1272,6 +1287,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[Any]" type B = tuple[*Ts] # E: All type parameters should be declared ("Ts" not declared) type C = Callable[P, None] # E: All type parameters should be declared ("P" not declared) [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695NonGenericAliasToGenericClass] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1399,6 +1415,8 @@ c1: A3[C, int] c2: A3[D, str] c3: A3[C, N] # E: Value of type variable "S" of "A3" cannot be "N" c4: A3[int, str] # E: Type argument "int" of "A3" must be a subtype of "C" +[builtins fixtures/type.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695TypeAliasInClassBodyOrFunction] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -1451,7 +1469,7 @@ class E[T]: self.a: A reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/type.pyi] [typing fixtures/typing-full.pyi] [case testPEP695RedefineAsTypeAlias1] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index e3f1b976d4e9..b7642d30efc8 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -270,14 +270,17 @@ def f() -> None: from typing import TypeVar def f() -> None: x = TypeVar('x') - x = 1 # E: Invalid assignment target - reveal_type(x) # N: Revealed type is "builtins.int" + x = 1 # E: Invalid assignment target \ + # E: Incompatible types in assignment (expression has type "int", variable has type "TypeVar") + reveal_type(x) # N: Revealed type is "typing.TypeVar" y = 1 # NOTE: '"int" not callable' is due to test stubs y = TypeVar('y') # E: Cannot redefine "y" as a type variable \ - # E: "int" not callable + # E: Incompatible types in assignment (expression has type "TypeVar", variable has type "int") def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testCannotRedefineVarAsModule] # flags: --allow-redefinition diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 5eea1fb2b53e..6f9e9eda1d02 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -93,11 +93,9 @@ T = TypeVar('T') A = Tuple[T, T] if int(): - A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ - # E: Value of type "int" is not indexable - # the second error is because of `Union = 0` in lib-stub/typing.pyi + A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation [builtins fixtures/tuple.pyi] -[out] +[typing fixtures/typing-full.pyi] [case testProhibitUsingVariablesAsTypesAndAllowAliasesAsTypes] @@ -1202,8 +1200,7 @@ unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expe reveal_type(unbound_tvt_alias2) # N: Revealed type is "def (*Any) -> builtins.str" class A(Generic[T]): - Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \ - # E: "T" is a type variable and only valid in type context + Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias x: A.Ta11 = {"a": 1} reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 0aff702e1b22..8f7dd12d9cd4 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1100,9 +1100,10 @@ reveal_type(t.fn) # N: Revealed type is "def (builtins.int, builtins.int, built [builtins fixtures/tuple.pyi] [case testVariadicNamedTuple] -from typing import Tuple, Callable, NamedTuple, Generic +from typing import Tuple, Callable, NamedTuple, Generic, TypeVar from typing_extensions import TypeVarTuple, Unpack +T = TypeVar("T") Ts = TypeVarTuple("Ts") class A(NamedTuple, Generic[Unpack[Ts], T]): fn: Callable[[Unpack[Ts]], None] @@ -1129,9 +1130,10 @@ nt2 = A(fn=bad, val=42) # E: Argument "fn" to "A" has incompatible type "Callab [builtins fixtures/tuple.pyi] [case testVariadicTypedDict] -from typing import Tuple, Callable, Generic +from typing import Tuple, Callable, Generic, TypeVar from typing_extensions import TypeVarTuple, Unpack, TypedDict +T = TypeVar("T") Ts = TypeVarTuple("Ts") class A(TypedDict, Generic[Unpack[Ts], T]): fn: Callable[[Unpack[Ts]], None] diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index b5fd85cb7ed8..a1b63077eef9 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -189,7 +189,7 @@ def g(x: int | str | tuple[int, str] | C) -> None: # flags: --python-version 3.9 from typing import Union def f(x: Union[int, str, None]) -> None: - if isinstance(x, int | str): # E: Unsupported left operand type for | ("Type[int]") + if isinstance(x, int | str): reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" else: reveal_type(x) # N: Revealed type is "None" diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index f46cfebb113f..3364dee6c696 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1443,7 +1443,13 @@ class C: pass class D: pass type E = D [out] + -> m + -> m -> m + -> m + -> m -> m -> m -> m +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 70cf427d6798..3970c8cacfbf 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -75,6 +75,7 @@ from typing import Union as B from builtins import tuple as B [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [out] == main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]") diff --git a/test-data/unit/fixtures/isinstance.pyi b/test-data/unit/fixtures/isinstance.pyi index c1125c24b941..c1446492af9b 100644 --- a/test-data/unit/fixtures/isinstance.pyi +++ b/test-data/unit/fixtures/isinstance.pyi @@ -5,8 +5,9 @@ T = TypeVar('T') class object: def __init__(self) -> None: pass -class type: +class type(Generic[T]): def __init__(self, x) -> None: pass + def __or__(self, other: type) -> type: pass class tuple(Generic[T]): pass diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index eb89de8c86ef..3b62d7fc1513 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -3,8 +3,8 @@ import _typeshed from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type -T = TypeVar("T") -Tco = TypeVar('Tco', covariant=True) +_T = TypeVar("_T") +_Tco = TypeVar('_Tco', covariant=True) class object: def __init__(self) -> None: pass @@ -12,17 +12,17 @@ class object: class type: def __init__(self, *a: object) -> None: pass def __call__(self, *a: object) -> object: pass -class tuple(Sequence[Tco], Generic[Tco]): - def __new__(cls: Type[T], iterable: Iterable[Tco] = ...) -> T: ... - def __iter__(self) -> Iterator[Tco]: pass +class tuple(Sequence[_Tco], Generic[_Tco]): + def __new__(cls: Type[_T], iterable: Iterable[_Tco] = ...) -> _T: ... + def __iter__(self) -> Iterator[_Tco]: pass def __contains__(self, item: object) -> bool: pass @overload - def __getitem__(self, x: int) -> Tco: pass + def __getitem__(self, x: int) -> _Tco: pass @overload - def __getitem__(self, x: slice) -> Tuple[Tco, ...]: ... - def __mul__(self, n: int) -> Tuple[Tco, ...]: pass - def __rmul__(self, n: int) -> Tuple[Tco, ...]: pass - def __add__(self, x: Tuple[Tco, ...]) -> Tuple[Tco, ...]: pass + def __getitem__(self, x: slice) -> Tuple[_Tco, ...]: ... + def __mul__(self, n: int) -> Tuple[_Tco, ...]: pass + def __rmul__(self, n: int) -> Tuple[_Tco, ...]: pass + def __add__(self, x: Tuple[_Tco, ...]) -> Tuple[_Tco, ...]: pass def count(self, obj: object) -> int: pass class function: __name__: str @@ -40,13 +40,13 @@ class str: pass # For convenience class bytes: pass class bytearray: pass -class list(Sequence[T], Generic[T]): +class list(Sequence[_T], Generic[_T]): @overload - def __getitem__(self, i: int) -> T: ... + def __getitem__(self, i: int) -> _T: ... @overload - def __getitem__(self, s: slice) -> list[T]: ... + def __getitem__(self, s: slice) -> list[_T]: ... def __contains__(self, item: object) -> bool: ... - def __iter__(self) -> Iterator[T]: ... + def __iter__(self) -> Iterator[_T]: ... def isinstance(x: object, t: type) -> bool: pass diff --git a/test-data/unit/fixtures/type.pyi b/test-data/unit/fixtures/type.pyi index 084b7f8388d8..4ae8ed9ca6b1 100644 --- a/test-data/unit/fixtures/type.pyi +++ b/test-data/unit/fixtures/type.pyi @@ -27,6 +27,7 @@ class bool: pass class int: pass class str: pass class ellipsis: pass +class float: pass if sys.version_info >= (3, 10): # type: ignore def isinstance(obj: object, class_or_tuple: type | types.UnionType, /) -> bool: ... diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 71d4dcb58853..9d61361fc16e 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -10,8 +10,11 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass -class _SpecialForm: ... -class TypeVar: ... +class _SpecialForm: + def __getitem__(self, index: Any) -> Any: ... +class TypeVar: + def __init__(self, name, *args, bound=None): ... + def __or__(self, other): ... class ParamSpec: ... class TypeVarTuple: ... @@ -19,12 +22,10 @@ def cast(t, o): ... def assert_type(o, t): ... overload = 0 Any = 0 -Union = 0 Optional = 0 Generic = 0 Protocol = 0 Tuple = 0 -Callable = 0 _promote = 0 Type = 0 no_type_check = 0 @@ -36,6 +37,8 @@ NoReturn = 0 NewType = 0 Self = 0 Unpack = 0 +Callable: _SpecialForm +Union: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -211,3 +214,5 @@ class TypeAliasType: def __init__( self, name: str, value: Any, *, type_params: Tuple[Union[TypeVar, ParamSpec, TypeVarTuple], ...] = () ) -> None: ... + + def __or__(self, other: Any) -> Any: ... diff --git a/test-data/unit/lib-stub/types.pyi b/test-data/unit/lib-stub/types.pyi index e4869dbc3093..dded0ba6cd9a 100644 --- a/test-data/unit/lib-stub/types.pyi +++ b/test-data/unit/lib-stub/types.pyi @@ -9,6 +9,8 @@ class ModuleType: __file__: str def __getattr__(self, name: str) -> Any: pass +class GenericAlias: ... + if sys.version_info >= (3, 10): class Union: def __or__(self, x) -> Union: ... diff --git a/test-data/unit/parse-python312.test b/test-data/unit/parse-python312.test index 28204ccd647b..90ee96f38deb 100644 --- a/test-data/unit/parse-python312.test +++ b/test-data/unit/parse-python312.test @@ -7,9 +7,12 @@ MypyFile:1( NameExpr(A) TypeParam( T) - IndexExpr:2( - NameExpr(C) - NameExpr(T)))) + LambdaExpr:2( + Block:-1( + ReturnStmt:2( + IndexExpr:2( + NameExpr(C) + NameExpr(T))))))) [case testPEP695GenericFunction] # mypy: enable-incomplete-feature=NewGenericSyntax