Skip to content

Commit

Permalink
Consider TypeVarTuple to be invariant (#16759)
Browse files Browse the repository at this point in the history
The TypeVarTuple equality checks mentioned in PEP 646 assume
TypeVarTuple to be `invariant`.
https://peps.python.org/pep-0646/#type-variable-tuple-equality

Fixes: #16739
  • Loading branch information
cdce8p authored Jan 30, 2024
1 parent 4183778 commit 7a746c4
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 7 deletions.
5 changes: 3 additions & 2 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -894,8 +894,9 @@ def visit_instance(self, template: Instance) -> list[Constraint]:
res.append(Constraint(template_arg, SUBTYPE_OF, suffix))
res.append(Constraint(template_arg, SUPERTYPE_OF, suffix))
elif isinstance(tvar, TypeVarTupleType):
# Handle variadic type variables covariantly for consistency.
res.extend(infer_constraints(template_arg, mapped_arg, self.direction))
# Consider variadic type variables to be invariant.
res.extend(infer_constraints(template_arg, mapped_arg, SUBTYPE_OF))
res.extend(infer_constraints(template_arg, mapped_arg, SUPERTYPE_OF))
return res
if (
template.type.is_protocol
Expand Down
24 changes: 19 additions & 5 deletions mypy/test/testconstraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ def test_basic_type_var_tuple_subtype(self) -> None:

def test_basic_type_var_tuple(self) -> None:
fx = self.fx
assert infer_constraints(
Instance(fx.gvi, [UnpackType(fx.ts)]), Instance(fx.gvi, [fx.a, fx.b]), SUPERTYPE_OF
) == [
assert set(
infer_constraints(
Instance(fx.gvi, [UnpackType(fx.ts)]), Instance(fx.gvi, [fx.a, fx.b]), SUPERTYPE_OF
)
) == {
Constraint(
type_var=fx.ts, op=SUPERTYPE_OF, target=TupleType([fx.a, fx.b], fx.std_tuple)
)
]
),
Constraint(
type_var=fx.ts, op=SUBTYPE_OF, target=TupleType([fx.a, fx.b], fx.std_tuple)
),
}

def test_type_var_tuple_with_prefix_and_suffix(self) -> None:
fx = self.fx
Expand All @@ -51,6 +56,9 @@ def test_type_var_tuple_with_prefix_and_suffix(self) -> None:
Constraint(
type_var=fx.ts, op=SUPERTYPE_OF, target=TupleType([fx.b, fx.c], fx.std_tuple)
),
Constraint(
type_var=fx.ts, op=SUBTYPE_OF, target=TupleType([fx.b, fx.c], fx.std_tuple)
),
Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.d),
}

Expand All @@ -64,7 +72,9 @@ def test_unpack_homogenous_tuple(self) -> None:
)
) == {
Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.a),
Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.a),
Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.b),
Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.b),
}

def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None:
Expand All @@ -78,7 +88,9 @@ def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None:
) == {
Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.a),
Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.b),
Constraint(type_var=fx.s, op=SUBTYPE_OF, target=fx.b),
Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.c),
Constraint(type_var=fx.s, op=SUBTYPE_OF, target=fx.c),
Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.d),
}

Expand All @@ -93,7 +105,9 @@ def test_unpack_with_prefix_and_suffix(self) -> None:
) == {
Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.a),
Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.b),
Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.b),
Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.c),
Constraint(type_var=fx.s, op=SUBTYPE_OF, target=fx.c),
Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.d),
}

Expand Down
27 changes: 27 additions & 0 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -2277,3 +2277,30 @@ higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Cal
higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
[builtins fixtures/tuple.pyi]

[case testTypeVarTupleInvariant]
from typing import Generic, Tuple
from typing_extensions import Unpack, TypeVarTuple
Ts = TypeVarTuple("Ts")

class Array(Generic[Unpack[Ts]]): ...

def pointwise_multiply(x: Array[Unpack[Ts]], y: Array[Unpack[Ts]]) -> Array[Unpack[Ts]]: ...

def a1(x: Array[int], y: Array[str], z: Array[int, str]) -> None:
reveal_type(pointwise_multiply(x, x)) # N: Revealed type is "__main__.Array[builtins.int]"
reveal_type(pointwise_multiply(x, y)) # E: Cannot infer type argument 1 of "pointwise_multiply" \
# N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]"
reveal_type(pointwise_multiply(x, z)) # E: Cannot infer type argument 1 of "pointwise_multiply" \
# N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]"

def func(x: Array[Unpack[Ts]], *args: Unpack[Ts]) -> Tuple[Unpack[Ts]]:
...

def a2(x: Array[int, str]) -> None:
reveal_type(func(x, 2, "Hello")) # N: Revealed type is "Tuple[builtins.int, builtins.str]"
reveal_type(func(x, 2)) # E: Cannot infer type argument 1 of "func" \
# N: Revealed type is "builtins.tuple[Any, ...]"
reveal_type(func(x, 2, "Hello", True)) # E: Cannot infer type argument 1 of "func" \
# N: Revealed type is "builtins.tuple[Any, ...]"
[builtins fixtures/tuple.pyi]

0 comments on commit 7a746c4

Please sign in to comment.