mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Fix reassignment of generic TypeAliasType treated as type expression

Open pareshjoshij opened this issue 1 month ago • 7 comments

Fixes #20308

Summary This PR fixes a false positive [type-arg] error when reassigning a generic PEP 695 TypeAliasType to a variable without type arguments.

The Fix Modified is_type_ref in semanal.py to return False when the target is a generic TypeAliasType used without subscripts. This forces the analyzer to treat it as a variable assignment rather than a type definition.

Verification I verified this locally with a reproduction script.

pareshjoshij avatar Nov 28 '25 10:11 pareshjoshij

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Nov 28 '25 11:11 github-actions[bot]

Would you mind adding tests for the new behaviour? test-data/unit/check-python312.test is a good place to add them

@hauntsaninja Thanks for the review.

I added the regression test to check-python312.test, but I am hitting a persistent test fixture error locally: AssertionError: Var(TypeVar).

It seems the test environment defines TypeVar as a Var, but checkexpr.py expects it to be a TypeInfo (class) when analyzing the TypeAliasType assignment.

I tried adding [builtins fixtures/list.pyi] and [builtins fixtures/dict.pyi] to the test case to load full definitions, but the assertion failure persists.

Could you advise on which fixture is required for testing PEP 695 TypeAliasType assignments?

Here is the test case I am adding:

[[case testPep695GenericTypeAliasReassignment]
from typing import reveal_type

type A[T] = T | str
B = A
reveal_type(B)
[out]
main:5: note: Revealed type is "typing.TypeAliasType"
[builtins fixtures/tuple.pyi]

pareshjoshij avatar Nov 29 '25 04:11 pareshjoshij

Would you mind adding tests for the new behaviour? test-data/unit/check-python312.test is a good place to add them

@hauntsaninja Thanks for the review.

I added the regression test to check-python312.test, but I am hitting a persistent test fixture error locally: AssertionError: Var(TypeVar).

It seems the test environment defines TypeVar as a Var, but checkexpr.py expects it to be a TypeInfo (class) when analyzing the TypeAliasType assignment.

I tried adding [builtins fixtures/list.pyi] and [builtins fixtures/dict.pyi] to the test case to load full definitions, but the assertion failure persists.

Could you advise on which fixture is required for testing PEP 695 TypeAliasType assignments?

Here is the test case I am adding:

[[case testPep695GenericTypeAliasReassignment]
from typing import reveal_type

type A[T] = T | str
B = A
reveal_type(B)
[out]
main:5: note: Revealed type is "typing.TypeAliasType"
[builtins fixtures/tuple.pyi]

Quick update: I managed to resolve the fixture issue mentioned above.

I found that explicitly adding [typing fixtures/typing-full.pyi] was required to ensure TypeVar is loaded as a TypeInfo (class) rather than a Var in the test environment. The regression test is now added and passing locally.

Please let me know if any further changes are required. Thanks for your kind attention to this matter!

pareshjoshij avatar Nov 29 '25 05:11 pareshjoshij

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Nov 29 '25 05:11 github-actions[bot]

Nice, thanks for adding the test cases and fighting the fixtures (they're the most annoying part about developing mypy).

Hmm, looking at your test case I think this prevents the use of B in type annotations entirely. That is, def foo(x: B) -> None: ... will now emit an error Variable "B" is not valid as a type. I'm guessing that's not what @jorenham expects

Maybe there is a solution that involves looking at whether the right hand side is a Python 3.12 type alias in typeanal.py, maybe somewhere around instantiate_type_alias

@hauntsaninja Thank you again for your patience and detailed guidance. I want to confirm the final implementation strategy before pushing:

I will revert the logic in semanal.py back to its original state.

The fix will be implemented inside instantiate_type_alias in typeanal.py. We will add a check there that, if a generic PEP 695 alias is used with zero arguments (act_len == 0), the function immediately returns the raw TypeAliasType(node, []) object, bypassing the argument count error.

My understanding is that this solves the original assignment issue while preserving B as a valid generic type for later use (e.g., x: B[int]).

Does this core strategy align with your expectations?

pareshjoshij avatar Nov 29 '25 07:11 pareshjoshij

Diff from mypy_primer, showing the effect of this PR on open source code:

scipy-stubs (https://github.com/scipy/scipy-stubs)
+ scipy-stubs/io/_fortran.pyi:38: error: Overloaded function signatures 2 and 3 overlap with incompatible return types  [overload-overlap]
+ scipy-stubs/io/_fortran.pyi:38: note: Flipping the order of overloads will fix this error
+ scipy-stubs/io/_fortran.pyi:44: error: Overloaded function signatures 2 and 3 overlap with incompatible return types  [overload-overlap]
+ scipy-stubs/io/_fortran.pyi:44: note: Flipping the order of overloads will fix this error
+ tests/datasets/test_utils.pyi:10: error: List item 0 has incompatible type "Callable[[], ndarray[tuple[int, int], dtype[unsignedinteger[_8Bit]]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:15: error: List item 0 has incompatible type "Callable[[], ndarray[tuple[int], dtype[float64]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:20: error: List item 0 has incompatible type overloaded function; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:24: error: List item 0 has incompatible type "Callable[[], ndarray[tuple[int, int], dtype[unsignedinteger[_8Bit]]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:24: error: List item 1 has incompatible type "Callable[[], ndarray[tuple[int], dtype[float64]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:24: error: List item 2 has incompatible type overloaded function; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]

github-actions[bot] avatar Dec 02 '25 06:12 github-actions[bot]

Diff from mypy_primer, showing the effect of this PR on open source code:

scipy-stubs (https://github.com/scipy/scipy-stubs)
+ scipy-stubs/io/_fortran.pyi:38: error: Overloaded function signatures 2 and 3 overlap with incompatible return types  [overload-overlap]
+ scipy-stubs/io/_fortran.pyi:38: note: Flipping the order of overloads will fix this error
+ scipy-stubs/io/_fortran.pyi:44: error: Overloaded function signatures 2 and 3 overlap with incompatible return types  [overload-overlap]
+ scipy-stubs/io/_fortran.pyi:44: note: Flipping the order of overloads will fix this error
+ tests/datasets/test_utils.pyi:10: error: List item 0 has incompatible type "Callable[[], ndarray[tuple[int, int], dtype[unsignedinteger[_8Bit]]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:15: error: List item 0 has incompatible type "Callable[[], ndarray[tuple[int], dtype[float64]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:20: error: List item 0 has incompatible type overloaded function; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:24: error: List item 0 has incompatible type "Callable[[], ndarray[tuple[int, int], dtype[unsignedinteger[_8Bit]]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:24: error: List item 1 has incompatible type "Callable[[], ndarray[tuple[int], dtype[float64]]]"; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]
+ tests/datasets/test_utils.pyi:24: error: List item 2 has incompatible type overloaded function; expected "Callable[[], ndarray[_NDT, dtype[_SCT]]]"  [list-item]

github-actions[bot] avatar Dec 02 '25 07:12 github-actions[bot]