From 825f14480343aba96918828d9178f6700a0e3890 Mon Sep 17 00:00:00 2001 From: kotlinisland <65446343+kotlinisland@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:58:09 +1000 Subject: [PATCH 1/2] specify that parameter specification should have variance --- .../mypy/generics_paramspec_variance.toml | 22 ++++++++++ .../pyrefly/generics_paramspec_variance.toml | 24 +++++++++++ .../pyright/generics_paramspec_variance.toml | 40 +++++++++++++++++ conformance/results/results.html | 7 +++ .../ty/generics_paramspec_variance.toml | 30 +++++++++++++ .../zuban/generics_paramspec_variance.toml | 22 ++++++++++ .../tests/generics_paramspec_variance.py | 43 +++++++++++++++++++ docs/spec/generics.rst | 11 +++-- 8 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 conformance/results/mypy/generics_paramspec_variance.toml create mode 100644 conformance/results/pyrefly/generics_paramspec_variance.toml create mode 100644 conformance/results/pyright/generics_paramspec_variance.toml create mode 100644 conformance/results/ty/generics_paramspec_variance.toml create mode 100644 conformance/results/zuban/generics_paramspec_variance.toml create mode 100644 conformance/tests/generics_paramspec_variance.py diff --git a/conformance/results/mypy/generics_paramspec_variance.toml b/conformance/results/mypy/generics_paramspec_variance.toml new file mode 100644 index 000000000..188439ca3 --- /dev/null +++ b/conformance/results/mypy/generics_paramspec_variance.toml @@ -0,0 +1,22 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 21: Expected 1 errors +Line 29: Expected 1 errors +Line 42: Expected 1 errors +Line 22: Unexpected errors ['generics_paramspec_variance.py:22: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment]'] +Line 24: Unexpected errors ['generics_paramspec_variance.py:24: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]'] +Line 28: Unexpected errors ['generics_paramspec_variance.py:28: error: Missing return statement [empty-body]'] +Line 34: Unexpected errors ['generics_paramspec_variance.py:34: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]'] +Line 43: Unexpected errors ['generics_paramspec_variance.py:43: error: Incompatible types in assignment (expression has type "CovariantParamSpecOld[[int]]", variable has type "CovariantParamSpecOld[[object]]") [assignment]'] +""" +output = """ +generics_paramspec_variance.py:13: error: Incompatible types in assignment (expression has type "ContravariantParamSpec[[int]]", variable has type "ContravariantParamSpec[[object]]") [assignment] +generics_paramspec_variance.py:22: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment] +generics_paramspec_variance.py:24: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc] +generics_paramspec_variance.py:28: error: Missing return statement [empty-body] +generics_paramspec_variance.py:31: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment] +generics_paramspec_variance.py:34: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc] +generics_paramspec_variance.py:38: error: Missing return statement [empty-body] +generics_paramspec_variance.py:43: error: Incompatible types in assignment (expression has type "CovariantParamSpecOld[[int]]", variable has type "CovariantParamSpecOld[[object]]") [assignment] +""" diff --git a/conformance/results/pyrefly/generics_paramspec_variance.toml b/conformance/results/pyrefly/generics_paramspec_variance.toml new file mode 100644 index 000000000..890b4d182 --- /dev/null +++ b/conformance/results/pyrefly/generics_paramspec_variance.toml @@ -0,0 +1,24 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 13: Expected 1 errors +Line 21: Expected 1 errors +Line 29: Expected 1 errors +Line 38: Expected 1 errors +Line 14: Unexpected errors ['`ContravariantParamSpec[[object]]` is not assignable to `ContravariantParamSpec[[int]]` [bad-assignment]'] +Line 22: Unexpected errors ['`CovariantParamSpec[[int]]` is not assignable to `CovariantParamSpec[[object]]` [bad-assignment]'] +Line 24: Unexpected errors ['Unexpected keyword argument `contravariant` to ParamSpec [invalid-param-spec]'] +Line 32: Unexpected errors ['`ContravariantParamSpecOld[[object]]` is not assignable to `ContravariantParamSpecOld[[int]]` [bad-assignment]'] +Line 34: Unexpected errors ['Unexpected keyword argument `covariant` to ParamSpec [invalid-param-spec]'] +Line 43: Unexpected errors ['`CovariantParamSpecOld[[int]]` is not assignable to `CovariantParamSpecOld[[object]]` [bad-assignment]'] +""" +output = """ +ERROR generics_paramspec_variance.py:14:39-71: `ContravariantParamSpec[[object]]` is not assignable to `ContravariantParamSpec[[int]]` [bad-assignment] +ERROR generics_paramspec_variance.py:22:39-64: `CovariantParamSpec[[int]]` is not assignable to `CovariantParamSpec[[object]]` [bad-assignment] +ERROR generics_paramspec_variance.py:24:24-42: Unexpected keyword argument `contravariant` to ParamSpec [invalid-param-spec] +ERROR generics_paramspec_variance.py:31:49-81: `ContravariantParamSpecOld[[int]]` is not assignable to `ContravariantParamSpecOld[[object]]` [bad-assignment] +ERROR generics_paramspec_variance.py:32:46-81: `ContravariantParamSpecOld[[object]]` is not assignable to `ContravariantParamSpecOld[[int]]` [bad-assignment] +ERROR generics_paramspec_variance.py:34:26-40: Unexpected keyword argument `covariant` to ParamSpec [invalid-param-spec] +ERROR generics_paramspec_variance.py:42:43-74: `CovariantParamSpecOld[[object]]` is not assignable to `CovariantParamSpecOld[[int]]` [bad-assignment] +ERROR generics_paramspec_variance.py:43:46-74: `CovariantParamSpecOld[[int]]` is not assignable to `CovariantParamSpecOld[[object]]` [bad-assignment] +""" diff --git a/conformance/results/pyright/generics_paramspec_variance.toml b/conformance/results/pyright/generics_paramspec_variance.toml new file mode 100644 index 000000000..df031a431 --- /dev/null +++ b/conformance/results/pyright/generics_paramspec_variance.toml @@ -0,0 +1,40 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 29: Expected 1 errors +Line 38: Expected 1 errors +Line 14: Unexpected errors ['generics_paramspec_variance.py:14:39 - error: Type "ContravariantParamSpec[(object)]" is not assignable to declared type "ContravariantParamSpec[(int)]"'] +Line 22: Unexpected errors ['generics_paramspec_variance.py:22:39 - error: Type "CovariantParamSpec[(int)]" is not assignable to declared type "CovariantParamSpec[(object)]"'] +Line 24: Unexpected errors ['generics_paramspec_variance.py:24:24 - error: "contravariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues)'] +Line 32: Unexpected errors ['generics_paramspec_variance.py:32:46 - error: Type "ContravariantParamSpecOld[(object)]" is not assignable to declared type "ContravariantParamSpecOld[(int)]"'] +Line 34: Unexpected errors ['generics_paramspec_variance.py:34:26 - error: "covariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues)'] +Line 43: Unexpected errors ['generics_paramspec_variance.py:43:46 - error: Type "CovariantParamSpecOld[(int)]" is not assignable to declared type "CovariantParamSpecOld[(object)]"'] +""" +output = """ +generics_paramspec_variance.py:13:42 - error: Type "ContravariantParamSpec[(int)]" is not assignable to declared type "ContravariantParamSpec[(object)]" +  "ContravariantParamSpec[(int)]" is not assignable to "ContravariantParamSpec[(object)]" +    Type parameter "InP@ContravariantParamSpec" is invariant, but "(int)" is not the same as "(object)" (reportAssignmentType) +generics_paramspec_variance.py:14:39 - error: Type "ContravariantParamSpec[(object)]" is not assignable to declared type "ContravariantParamSpec[(int)]" +  "ContravariantParamSpec[(object)]" is not assignable to "ContravariantParamSpec[(int)]" +    Type parameter "InP@ContravariantParamSpec" is invariant, but "(object)" is not the same as "(int)" (reportAssignmentType) +generics_paramspec_variance.py:21:36 - error: Type "CovariantParamSpec[(object)]" is not assignable to declared type "CovariantParamSpec[(int)]" +  "CovariantParamSpec[(object)]" is not assignable to "CovariantParamSpec[(int)]" +    Type parameter "OutP@CovariantParamSpec" is invariant, but "(object)" is not the same as "(int)" (reportAssignmentType) +generics_paramspec_variance.py:22:39 - error: Type "CovariantParamSpec[(int)]" is not assignable to declared type "CovariantParamSpec[(object)]" +  "CovariantParamSpec[(int)]" is not assignable to "CovariantParamSpec[(object)]" +    Type parameter "OutP@CovariantParamSpec" is invariant, but "(int)" is not the same as "(object)" (reportAssignmentType) +generics_paramspec_variance.py:24:24 - error: "contravariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues) +generics_paramspec_variance.py:31:49 - error: Type "ContravariantParamSpecOld[(int)]" is not assignable to declared type "ContravariantParamSpecOld[(object)]" +  "ContravariantParamSpecOld[(int)]" is not assignable to "ContravariantParamSpecOld[(object)]" +    Type parameter "InP@ContravariantParamSpecOld" is invariant, but "(int)" is not the same as "(object)" (reportAssignmentType) +generics_paramspec_variance.py:32:46 - error: Type "ContravariantParamSpecOld[(object)]" is not assignable to declared type "ContravariantParamSpecOld[(int)]" +  "ContravariantParamSpecOld[(object)]" is not assignable to "ContravariantParamSpecOld[(int)]" +    Type parameter "InP@ContravariantParamSpecOld" is invariant, but "(object)" is not the same as "(int)" (reportAssignmentType) +generics_paramspec_variance.py:34:26 - error: "covariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues) +generics_paramspec_variance.py:42:43 - error: Type "CovariantParamSpecOld[(object)]" is not assignable to declared type "CovariantParamSpecOld[(int)]" +  "CovariantParamSpecOld[(object)]" is not assignable to "CovariantParamSpecOld[(int)]" +    Type parameter "OutP@CovariantParamSpecOld" is invariant, but "(object)" is not the same as "(int)" (reportAssignmentType) +generics_paramspec_variance.py:43:46 - error: Type "CovariantParamSpecOld[(int)]" is not assignable to declared type "CovariantParamSpecOld[(object)]" +  "CovariantParamSpecOld[(int)]" is not assignable to "CovariantParamSpecOld[(object)]" +    Type parameter "OutP@CovariantParamSpecOld" is invariant, but "(int)" is not the same as "(object)" (reportAssignmentType) +""" diff --git a/conformance/results/results.html b/conformance/results/results.html index 3ddb751cd..9dad0c6aa 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -335,6 +335,13 @@

Python Type System Conformance Test Results

Pass Pass +     generics_paramspec_variance +Unsupported +Unsupported +Unsupported +Unsupported +Unsupported +      generics_scoping Pass Pass diff --git a/conformance/results/ty/generics_paramspec_variance.toml b/conformance/results/ty/generics_paramspec_variance.toml new file mode 100644 index 000000000..8e48da703 --- /dev/null +++ b/conformance/results/ty/generics_paramspec_variance.toml @@ -0,0 +1,30 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 13: Expected 1 errors +Line 21: Expected 1 errors +Line 31: Expected 1 errors +Line 42: Expected 1 errors +Line 24: Unexpected errors ['generics_paramspec_variance.py:24:7: error[invalid-paramspec] The variance and bound arguments for `ParamSpec` do not have defined semantics yet'] +Line 27: Unexpected errors ['generics_paramspec_variance.py:27:33: error[invalid-argument-type] `ParamSpec` is not a valid argument to `Generic`'] +Line 28: Unexpected errors ['generics_paramspec_variance.py:28:23: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `(...) -> Unknown`', 'generics_paramspec_variance.py:28:32: error[invalid-type-form] Variable of type `ParamSpec` is not allowed in a return type annotation', 'generics_paramspec_variance.py:28:32: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`'] +Line 34: Unexpected errors ['generics_paramspec_variance.py:34:8: error[invalid-paramspec] The variance and bound arguments for `ParamSpec` do not have defined semantics yet'] +Line 37: Unexpected errors ['generics_paramspec_variance.py:37:29: error[invalid-argument-type] `ParamSpec` is not a valid argument to `Generic`'] +Line 39: Unexpected errors ['generics_paramspec_variance.py:39:34: error[invalid-type-form] Variable of type `ParamSpec` is not allowed in a parameter annotation', 'generics_paramspec_variance.py:39:34: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`'] +""" +output = """ +generics_paramspec_variance.py:24:7: error[invalid-paramspec] The variance and bound arguments for `ParamSpec` do not have defined semantics yet +generics_paramspec_variance.py:27:33: error[invalid-argument-type] `ParamSpec` is not a valid argument to `Generic` +generics_paramspec_variance.py:28:23: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `(...) -> Unknown` +generics_paramspec_variance.py:28:32: error[invalid-type-form] Variable of type `ParamSpec` is not allowed in a return type annotation +generics_paramspec_variance.py:28:32: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +generics_paramspec_variance.py:29:34: error[invalid-type-form] Variable of type `ParamSpec` is not allowed in a parameter annotation +generics_paramspec_variance.py:29:34: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +generics_paramspec_variance.py:34:8: error[invalid-paramspec] The variance and bound arguments for `ParamSpec` do not have defined semantics yet +generics_paramspec_variance.py:37:29: error[invalid-argument-type] `ParamSpec` is not a valid argument to `Generic` +generics_paramspec_variance.py:38:23: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `(...) -> Unknown` +generics_paramspec_variance.py:38:32: error[invalid-type-form] Variable of type `ParamSpec` is not allowed in a return type annotation +generics_paramspec_variance.py:38:32: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +generics_paramspec_variance.py:39:34: error[invalid-type-form] Variable of type `ParamSpec` is not allowed in a parameter annotation +generics_paramspec_variance.py:39:34: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +""" diff --git a/conformance/results/zuban/generics_paramspec_variance.toml b/conformance/results/zuban/generics_paramspec_variance.toml new file mode 100644 index 000000000..188439ca3 --- /dev/null +++ b/conformance/results/zuban/generics_paramspec_variance.toml @@ -0,0 +1,22 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 21: Expected 1 errors +Line 29: Expected 1 errors +Line 42: Expected 1 errors +Line 22: Unexpected errors ['generics_paramspec_variance.py:22: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment]'] +Line 24: Unexpected errors ['generics_paramspec_variance.py:24: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]'] +Line 28: Unexpected errors ['generics_paramspec_variance.py:28: error: Missing return statement [empty-body]'] +Line 34: Unexpected errors ['generics_paramspec_variance.py:34: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]'] +Line 43: Unexpected errors ['generics_paramspec_variance.py:43: error: Incompatible types in assignment (expression has type "CovariantParamSpecOld[[int]]", variable has type "CovariantParamSpecOld[[object]]") [assignment]'] +""" +output = """ +generics_paramspec_variance.py:13: error: Incompatible types in assignment (expression has type "ContravariantParamSpec[[int]]", variable has type "ContravariantParamSpec[[object]]") [assignment] +generics_paramspec_variance.py:22: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment] +generics_paramspec_variance.py:24: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc] +generics_paramspec_variance.py:28: error: Missing return statement [empty-body] +generics_paramspec_variance.py:31: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment] +generics_paramspec_variance.py:34: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc] +generics_paramspec_variance.py:38: error: Missing return statement [empty-body] +generics_paramspec_variance.py:43: error: Incompatible types in assignment (expression has type "CovariantParamSpecOld[[int]]", variable has type "CovariantParamSpecOld[[object]]") [assignment] +""" diff --git a/conformance/tests/generics_paramspec_variance.py b/conformance/tests/generics_paramspec_variance.py new file mode 100644 index 000000000..59c90b758 --- /dev/null +++ b/conformance/tests/generics_paramspec_variance.py @@ -0,0 +1,43 @@ +""" +Tests variance of ParamSpec. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#semantics + + +from typing import Callable, Generic, ParamSpec + +class ContravariantParamSpec[**InP]: + def f(self, *args: InP.args, **kwargs: InP.kwargs): ... + +in_obj: ContravariantParamSpec[object] = ContravariantParamSpec[int]() # E +in_int: ContravariantParamSpec[int] = ContravariantParamSpec[object]() # OK + + +class CovariantParamSpec[**OutP]: + def f(self, fn: Callable[OutP, None]) -> None: ... + + +out_int: CovariantParamSpec[int] = CovariantParamSpec[object]() # E +out_obj: CovariantParamSpec[object] = CovariantParamSpec[int]() # OK + +InP = ParamSpec("InP", contravariant=True) + + +class ContravariantParamSpecOld(Generic[InP]): + def in_f(self) -> Callable[InP, None]: ... # OK + def out_f(self, fn: Callable[InP, None]) -> None: ... # E + +in_obj_old: ContravariantParamSpecOld[object] = ContravariantParamSpecOld[int]() # E +in_int_old: ContravariantParamSpecOld[int] = ContravariantParamSpecOld[object]() # OK + +OutP = ParamSpec("OutP", covariant=True) + + +class CovariantParamSpecOld(Generic[OutP]): + def in_f(self) -> Callable[OutP, None]: ... # E + def out_f(self, fn: Callable[OutP, None]) -> None: ... # OK + + +out_int_old: CovariantParamSpecOld[int] = CovariantParamSpecOld[object]() # E +out_obj_old: CovariantParamSpecOld[object] = CovariantParamSpecOld[int]() # OK diff --git a/docs/spec/generics.rst b/docs/spec/generics.rst index c1fa05325..bf6ba9e66 100644 --- a/docs/spec/generics.rst +++ b/docs/spec/generics.rst @@ -2712,13 +2712,12 @@ The algorithm for computing the variance of a type parameter is as follows. For each type parameter in a generic class: -1. If the type parameter is variadic (``TypeVarTuple``) or a parameter -specification (``ParamSpec``), it is always considered invariant. No further -inference is needed. +1. If the type parameter is variadic (``TypeVarTuple``) it is always +considered invariant. No further inference is needed. -2. If the type parameter comes from a traditional ``TypeVar`` declaration and -is not specified as ``infer_variance`` (see below), its variance is specified -by the ``TypeVar`` constructor call. No further inference is needed. +2. If the type parameter comes from a traditional ``TypeVar``/``ParamSpec`` +declaration and is not specified as ``infer_variance`` (see below), its +variance is specified by the constructor call. No further inference is needed. 3. Create two specialized versions of the class. We'll refer to these as ``upper`` and ``lower`` specializations. In both of these specializations, From 5788125c68b70d6379d07358f911eabcbba8d0ce Mon Sep 17 00:00:00 2001 From: kotlinisland <65446343+kotlinisland@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:58:52 +1000 Subject: [PATCH 2/2] specify that type variable tuple should have variance --- .../mypy/generics_typevartuple_variance.toml | 24 ++++++++++ .../generics_typevartuple_variance.toml | 24 ++++++++++ .../generics_typevartuple_variance.toml | 40 +++++++++++++++++ conformance/results/results.html | 7 +++ .../ty/generics_typevartuple_variance.toml | 28 ++++++++++++ .../zuban/generics_typevartuple_variance.toml | 24 ++++++++++ .../tests/generics_typevartuple_variance.py | 44 +++++++++++++++++++ docs/spec/generics.rst | 17 +++---- 8 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 conformance/results/mypy/generics_typevartuple_variance.toml create mode 100644 conformance/results/pyrefly/generics_typevartuple_variance.toml create mode 100644 conformance/results/pyright/generics_typevartuple_variance.toml create mode 100644 conformance/results/ty/generics_typevartuple_variance.toml create mode 100644 conformance/results/zuban/generics_typevartuple_variance.toml create mode 100644 conformance/tests/generics_typevartuple_variance.py diff --git a/conformance/results/mypy/generics_typevartuple_variance.toml b/conformance/results/mypy/generics_typevartuple_variance.toml new file mode 100644 index 000000000..b9869a7d4 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_variance.toml @@ -0,0 +1,24 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 13: Expected 1 errors +Line 32: Expected 1 errors +Line 39: Expected 1 errors +Line 14: Unexpected errors ['generics_typevartuple_variance.py:14: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTuple[object]", variable has type "ContravariantTypeVarTuple[int]") [assignment]'] +Line 18: Unexpected errors ['generics_typevartuple_variance.py:18: error: Missing return statement [empty-body]'] +Line 25: Unexpected errors ['generics_typevartuple_variance.py:25: error: Unexpected keyword argument "contravariant" for "TypeVarTuple" [misc]'] +Line 33: Unexpected errors ['generics_typevartuple_variance.py:33: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTupleOld[object]", variable has type "ContravariantTypeVarTupleOld[int]") [assignment]'] +Line 35: Unexpected errors ['generics_typevartuple_variance.py:35: error: Unexpected keyword argument "covariant" for "TypeVarTuple" [misc]'] +Line 40: Unexpected errors ['generics_typevartuple_variance.py:40: error: Missing return statement [empty-body]'] +""" +output = """ +generics_typevartuple_variance.py:14: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTuple[object]", variable has type "ContravariantTypeVarTuple[int]") [assignment] +generics_typevartuple_variance.py:18: error: Missing return statement [empty-body] +generics_typevartuple_variance.py:21: error: Incompatible types in assignment (expression has type "CovariantTypeVarTuple[object]", variable has type "CovariantTypeVarTuple[int]") [assignment] +generics_typevartuple_variance.py:25: error: Unexpected keyword argument "contravariant" for "TypeVarTuple" [misc] +generics_typevartuple_variance.py:30: error: Missing return statement [empty-body] +generics_typevartuple_variance.py:33: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTupleOld[object]", variable has type "ContravariantTypeVarTupleOld[int]") [assignment] +generics_typevartuple_variance.py:35: error: Unexpected keyword argument "covariant" for "TypeVarTuple" [misc] +generics_typevartuple_variance.py:40: error: Missing return statement [empty-body] +generics_typevartuple_variance.py:43: error: Incompatible types in assignment (expression has type "CovariantTypeVarTupleOld[object]", variable has type "CovariantTypeVarTupleOld[int]") [assignment] +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_variance.toml b/conformance/results/pyrefly/generics_typevartuple_variance.toml new file mode 100644 index 000000000..d82f805d7 --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_variance.toml @@ -0,0 +1,24 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 30: Expected 1 errors +Line 39: Expected 1 errors +Line 14: Unexpected errors ['`ContravariantTypeVarTuple[object]` is not assignable to `ContravariantTypeVarTuple[int]` [bad-assignment]'] +Line 22: Unexpected errors ['`CovariantTypeVarTuple[int]` is not assignable to `CovariantTypeVarTuple[object]` [bad-assignment]'] +Line 25: Unexpected errors ['Unexpected keyword argument `contravariant` to TypeVarTuple [invalid-type-var-tuple]'] +Line 33: Unexpected errors ['`ContravariantTypeVarTupleOld[object]` is not assignable to `ContravariantTypeVarTupleOld[int]` [bad-assignment]'] +Line 35: Unexpected errors ['Unexpected keyword argument `covariant` to TypeVarTuple [invalid-type-var-tuple]'] +Line 44: Unexpected errors ['`CovariantTypeVarTupleOld[int]` is not assignable to `CovariantTypeVarTupleOld[object]` [bad-assignment]'] +""" +output = """ +ERROR generics_typevartuple_variance.py:13:45-77: `ContravariantTypeVarTuple[int]` is not assignable to `ContravariantTypeVarTuple[object]` [bad-assignment] +ERROR generics_typevartuple_variance.py:14:42-77: `ContravariantTypeVarTuple[object]` is not assignable to `ContravariantTypeVarTuple[int]` [bad-assignment] +ERROR generics_typevartuple_variance.py:21:39-70: `CovariantTypeVarTuple[object]` is not assignable to `CovariantTypeVarTuple[int]` [bad-assignment] +ERROR generics_typevartuple_variance.py:22:42-70: `CovariantTypeVarTuple[int]` is not assignable to `CovariantTypeVarTuple[object]` [bad-assignment] +ERROR generics_typevartuple_variance.py:25:29-47: Unexpected keyword argument `contravariant` to TypeVarTuple [invalid-type-var-tuple] +ERROR generics_typevartuple_variance.py:32:52-87: `ContravariantTypeVarTupleOld[int]` is not assignable to `ContravariantTypeVarTupleOld[object]` [bad-assignment] +ERROR generics_typevartuple_variance.py:33:49-87: `ContravariantTypeVarTupleOld[object]` is not assignable to `ContravariantTypeVarTupleOld[int]` [bad-assignment] +ERROR generics_typevartuple_variance.py:35:31-45: Unexpected keyword argument `covariant` to TypeVarTuple [invalid-type-var-tuple] +ERROR generics_typevartuple_variance.py:43:46-80: `CovariantTypeVarTupleOld[object]` is not assignable to `CovariantTypeVarTupleOld[int]` [bad-assignment] +ERROR generics_typevartuple_variance.py:44:49-80: `CovariantTypeVarTupleOld[int]` is not assignable to `CovariantTypeVarTupleOld[object]` [bad-assignment] +""" diff --git a/conformance/results/pyright/generics_typevartuple_variance.toml b/conformance/results/pyright/generics_typevartuple_variance.toml new file mode 100644 index 000000000..d604ca859 --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_variance.toml @@ -0,0 +1,40 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 30: Expected 1 errors +Line 39: Expected 1 errors +Line 14: Unexpected errors ['generics_typevartuple_variance.py:14:42 - error: Type "ContravariantTypeVarTuple[object]" is not assignable to declared type "ContravariantTypeVarTuple[int]"'] +Line 22: Unexpected errors ['generics_typevartuple_variance.py:22:42 - error: Type "CovariantTypeVarTuple[int]" is not assignable to declared type "CovariantTypeVarTuple[object]"'] +Line 25: Unexpected errors ['generics_typevartuple_variance.py:25:29 - error: "contravariant" is unknown parameter to TypeVarTuple (reportGeneralTypeIssues)'] +Line 33: Unexpected errors ['generics_typevartuple_variance.py:33:49 - error: Type "ContravariantTypeVarTupleOld[object]" is not assignable to declared type "ContravariantTypeVarTupleOld[int]"'] +Line 35: Unexpected errors ['generics_typevartuple_variance.py:35:31 - error: "covariant" is unknown parameter to TypeVarTuple (reportGeneralTypeIssues)'] +Line 44: Unexpected errors ['generics_typevartuple_variance.py:44:49 - error: Type "CovariantTypeVarTupleOld[int]" is not assignable to declared type "CovariantTypeVarTupleOld[object]"'] +""" +output = """ +generics_typevartuple_variance.py:13:45 - error: Type "ContravariantTypeVarTuple[int]" is not assignable to declared type "ContravariantTypeVarTuple[object]" +  "ContravariantTypeVarTuple[int]" is not assignable to "ContravariantTypeVarTuple[object]" +    Type parameter "InTs@ContravariantTypeVarTuple" is invariant, but "*tuple[int]" is not the same as "*tuple[object]" (reportAssignmentType) +generics_typevartuple_variance.py:14:42 - error: Type "ContravariantTypeVarTuple[object]" is not assignable to declared type "ContravariantTypeVarTuple[int]" +  "ContravariantTypeVarTuple[object]" is not assignable to "ContravariantTypeVarTuple[int]" +    Type parameter "InTs@ContravariantTypeVarTuple" is invariant, but "*tuple[object]" is not the same as "*tuple[int]" (reportAssignmentType) +generics_typevartuple_variance.py:21:39 - error: Type "CovariantTypeVarTuple[object]" is not assignable to declared type "CovariantTypeVarTuple[int]" +  "CovariantTypeVarTuple[object]" is not assignable to "CovariantTypeVarTuple[int]" +    Type parameter "OutTs@CovariantTypeVarTuple" is invariant, but "*tuple[object]" is not the same as "*tuple[int]" (reportAssignmentType) +generics_typevartuple_variance.py:22:42 - error: Type "CovariantTypeVarTuple[int]" is not assignable to declared type "CovariantTypeVarTuple[object]" +  "CovariantTypeVarTuple[int]" is not assignable to "CovariantTypeVarTuple[object]" +    Type parameter "OutTs@CovariantTypeVarTuple" is invariant, but "*tuple[int]" is not the same as "*tuple[object]" (reportAssignmentType) +generics_typevartuple_variance.py:25:29 - error: "contravariant" is unknown parameter to TypeVarTuple (reportGeneralTypeIssues) +generics_typevartuple_variance.py:32:52 - error: Type "ContravariantTypeVarTupleOld[int]" is not assignable to declared type "ContravariantTypeVarTupleOld[object]" +  "ContravariantTypeVarTupleOld[int]" is not assignable to "ContravariantTypeVarTupleOld[object]" +    Type parameter "InTs@ContravariantTypeVarTupleOld" is invariant, but "*tuple[int]" is not the same as "*tuple[object]" (reportAssignmentType) +generics_typevartuple_variance.py:33:49 - error: Type "ContravariantTypeVarTupleOld[object]" is not assignable to declared type "ContravariantTypeVarTupleOld[int]" +  "ContravariantTypeVarTupleOld[object]" is not assignable to "ContravariantTypeVarTupleOld[int]" +    Type parameter "InTs@ContravariantTypeVarTupleOld" is invariant, but "*tuple[object]" is not the same as "*tuple[int]" (reportAssignmentType) +generics_typevartuple_variance.py:35:31 - error: "covariant" is unknown parameter to TypeVarTuple (reportGeneralTypeIssues) +generics_typevartuple_variance.py:43:46 - error: Type "CovariantTypeVarTupleOld[object]" is not assignable to declared type "CovariantTypeVarTupleOld[int]" +  "CovariantTypeVarTupleOld[object]" is not assignable to "CovariantTypeVarTupleOld[int]" +    Type parameter "OutTs@CovariantTypeVarTupleOld" is invariant, but "*tuple[object]" is not the same as "*tuple[int]" (reportAssignmentType) +generics_typevartuple_variance.py:44:49 - error: Type "CovariantTypeVarTupleOld[int]" is not assignable to declared type "CovariantTypeVarTupleOld[object]" +  "CovariantTypeVarTupleOld[int]" is not assignable to "CovariantTypeVarTupleOld[object]" +    Type parameter "OutTs@CovariantTypeVarTupleOld" is invariant, but "*tuple[int]" is not the same as "*tuple[object]" (reportAssignmentType) +""" diff --git a/conformance/results/results.html b/conformance/results/results.html index 9dad0c6aa..0f8c1c35d 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -468,6 +468,13 @@

Python Type System Conformance Test Results

Pass Unsupported +     generics_typevartuple_variance +Unsupported +Unsupported +Unsupported +Unsupported +Unsupported +      generics_upper_bound
Partial

Does not reject use of type variable within an upper bound.

Pass diff --git a/conformance/results/ty/generics_typevartuple_variance.toml b/conformance/results/ty/generics_typevartuple_variance.toml new file mode 100644 index 000000000..8c726b6df --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_variance.toml @@ -0,0 +1,28 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 32: Expected 1 errors +Line 39: Expected 1 errors +Line 43: Expected 1 errors +Line 14: Unexpected errors ["generics_typevartuple_variance.py:14:9: error[not-subscriptable] Cannot subscript non-generic type ``", "generics_typevartuple_variance.py:14:42: error[not-subscriptable] Cannot subscript non-generic type ``"] +Line 18: Unexpected errors ['generics_typevartuple_variance.py:18:20: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `tuple[@Todo(TypeVarTuple), ...]`'] +Line 22: Unexpected errors ["generics_typevartuple_variance.py:22:10: error[not-subscriptable] Cannot subscript non-generic type ``", "generics_typevartuple_variance.py:22:42: error[not-subscriptable] Cannot subscript non-generic type ``"] +Line 25: Unexpected errors ['generics_typevartuple_variance.py:25:29: error[unknown-argument] Argument `contravariant` does not match any known parameter of function `__new__`'] +Line 35: Unexpected errors ['generics_typevartuple_variance.py:35:31: error[unknown-argument] Argument `covariant` does not match any known parameter of function `__new__`'] +Line 40: Unexpected errors ['generics_typevartuple_variance.py:40:24: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `tuple[@Todo(TypeVarTuple), ...]`'] +""" +output = """ +generics_typevartuple_variance.py:13:9: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:13:45: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:14:9: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:14:42: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:18:20: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `tuple[@Todo(TypeVarTuple), ...]` +generics_typevartuple_variance.py:21:10: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:21:39: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:22:10: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:22:42: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_variance.py:25:29: error[unknown-argument] Argument `contravariant` does not match any known parameter of function `__new__` +generics_typevartuple_variance.py:30:24: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `tuple[@Todo(TypeVarTuple), ...]` +generics_typevartuple_variance.py:35:31: error[unknown-argument] Argument `covariant` does not match any known parameter of function `__new__` +generics_typevartuple_variance.py:40:24: error[empty-body] Function always implicitly returns `None`, which is not assignable to return type `tuple[@Todo(TypeVarTuple), ...]` +""" diff --git a/conformance/results/zuban/generics_typevartuple_variance.toml b/conformance/results/zuban/generics_typevartuple_variance.toml new file mode 100644 index 000000000..c1482b688 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_variance.toml @@ -0,0 +1,24 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 13: Expected 1 errors +Line 32: Expected 1 errors +Line 39: Expected 1 errors +Line 14: Unexpected errors ['generics_typevartuple_variance.py:14: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTuple[object]", variable has type "ContravariantTypeVarTuple[int]") [assignment]'] +Line 18: Unexpected errors ['generics_typevartuple_variance.py:18: error: Missing return statement [empty-body]'] +Line 25: Unexpected errors ['generics_typevartuple_variance.py:25: error: Unexpected keyword argument "contravariant" for "TypeVarTuple" [call-arg]'] +Line 33: Unexpected errors ['generics_typevartuple_variance.py:33: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTupleOld[object]", variable has type "ContravariantTypeVarTupleOld[int]") [assignment]'] +Line 35: Unexpected errors ['generics_typevartuple_variance.py:35: error: Unexpected keyword argument "covariant" for "TypeVarTuple" [call-arg]'] +Line 40: Unexpected errors ['generics_typevartuple_variance.py:40: error: Missing return statement [empty-body]'] +""" +output = """ +generics_typevartuple_variance.py:14: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTuple[object]", variable has type "ContravariantTypeVarTuple[int]") [assignment] +generics_typevartuple_variance.py:18: error: Missing return statement [empty-body] +generics_typevartuple_variance.py:21: error: Incompatible types in assignment (expression has type "CovariantTypeVarTuple[object]", variable has type "CovariantTypeVarTuple[int]") [assignment] +generics_typevartuple_variance.py:25: error: Unexpected keyword argument "contravariant" for "TypeVarTuple" [call-arg] +generics_typevartuple_variance.py:30: error: Missing return statement [empty-body] +generics_typevartuple_variance.py:33: error: Incompatible types in assignment (expression has type "ContravariantTypeVarTupleOld[object]", variable has type "ContravariantTypeVarTupleOld[int]") [assignment] +generics_typevartuple_variance.py:35: error: Unexpected keyword argument "covariant" for "TypeVarTuple" [call-arg] +generics_typevartuple_variance.py:40: error: Missing return statement [empty-body] +generics_typevartuple_variance.py:43: error: Incompatible types in assignment (expression has type "CovariantTypeVarTupleOld[object]", variable has type "CovariantTypeVarTupleOld[int]") [assignment] +""" diff --git a/conformance/tests/generics_typevartuple_variance.py b/conformance/tests/generics_typevartuple_variance.py new file mode 100644 index 000000000..e6d92d6b4 --- /dev/null +++ b/conformance/tests/generics_typevartuple_variance.py @@ -0,0 +1,44 @@ +""" +Tests variance of TypeVarTuple. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#semantics + + +from typing import Generic, TypeVarTuple + +class ContravariantTypeVarTuple[*InTs]: + def f(self, t: tuple[*InTs]): ... + +in_obj: ContravariantTypeVarTuple[object] = ContravariantTypeVarTuple[int]() # E +in_int: ContravariantTypeVarTuple[int] = ContravariantTypeVarTuple[object]() # OK + + +class CovariantTypeVarTuple[*OutTs]: + def f(self) -> tuple[*OutTs]: ... + + +out_int: CovariantTypeVarTuple[int] = CovariantTypeVarTuple[object]() # E +out_obj: CovariantTypeVarTuple[object] = CovariantTypeVarTuple[int]() # OK + + +InTs = TypeVarTuple("InTs", contravariant=True) + + +class ContravariantTypeVarTupleOld(Generic[*InTs]): + def in_f(self, *args: *InTs) -> None: ... # OK + def out_f(self) -> tuple[*InTs]: ... # E + +in_obj_old: ContravariantTypeVarTupleOld[object] = ContravariantTypeVarTupleOld[int]() # E +in_int_old: ContravariantTypeVarTupleOld[int] = ContravariantTypeVarTupleOld[object]() # OK + +OutTs = TypeVarTuple("OutTs", covariant=True) + + +class CovariantTypeVarTupleOld(Generic[*OutTs]): + def in_f(self, *args: *OutTs) -> None: ... # E + def out_f(self) -> tuple[*OutTs]: ... # OK + + +out_int_old: CovariantTypeVarTupleOld[int] = CovariantTypeVarTupleOld[object]() # E +out_obj_old: CovariantTypeVarTupleOld[object] = CovariantTypeVarTupleOld[int]() # OK diff --git a/docs/spec/generics.rst b/docs/spec/generics.rst index bf6ba9e66..887e87bf1 100644 --- a/docs/spec/generics.rst +++ b/docs/spec/generics.rst @@ -1189,12 +1189,11 @@ for two reasons: * To improve readability: the star also functions as an explicit visual indicator that the type variable tuple is not a normal type variable. -Variance, Type Constraints and Type Bounds: Not Supported +Type Constraints and Type Bounds: Not Supported """"""""""""""""""""""""""""""""""""""""""""""""""""""""" ``TypeVarTuple`` does not currently support specification of: -* Variance (e.g. ``TypeVar('T', covariant=True)``) * Type constraints (``TypeVar('T', int, float)``) * Type bounds (``TypeVar('T', bound=ParentClass)``) @@ -2712,14 +2711,12 @@ The algorithm for computing the variance of a type parameter is as follows. For each type parameter in a generic class: -1. If the type parameter is variadic (``TypeVarTuple``) it is always -considered invariant. No further inference is needed. +1. If the type parameter comes from a traditional +``TypeVar``/``TypeVarTuple``/``ParamSpec`` declaration and is not specified +as ``infer_variance`` (see below), its variance is specified by the +constructor call. No further inference is needed. -2. If the type parameter comes from a traditional ``TypeVar``/``ParamSpec`` -declaration and is not specified as ``infer_variance`` (see below), its -variance is specified by the constructor call. No further inference is needed. - -3. Create two specialized versions of the class. We'll refer to these as +2. Create two specialized versions of the class. We'll refer to these as ``upper`` and ``lower`` specializations. In both of these specializations, replace all type parameters other than the one being inferred by a dummy type instance (a concrete anonymous class that is assumed to meet the bounds or @@ -2729,7 +2726,7 @@ specialization ignores the type parameter's upper bound or constraints. In the ``lower`` specialized class, specialize the target type parameter with itself (i.e. the corresponding type argument is the type parameter itself). -4. Determine whether ``lower`` can be assigned to ``upper`` using normal +3. Determine whether ``lower`` can be assigned to ``upper`` using normal assignability rules. If so, the target type parameter is covariant. If not, determine whether ``upper`` can be assigned to ``lower``. If so, the target type parameter is contravariant. If neither of these combinations are