Preserve Literal types through TypeVar solving for unions#2883
Preserve Literal types through TypeVar solving for unions#2883migeed-z wants to merge 2 commits intofacebook:mainfrom
Conversation
Summary: Iterating over a tuple of literals with `enumerate()` promotes the Literal types to their base type (e.g., `Literal["a"]` becomes `str`), while direct iteration preserves them. This is because `enumerate[T]` solves `T` via TypeVar solving, which promotes implicit literals. Adds a bug-marked test capturing the current behavior. Related: facebook#1323 Differential Revision: D97841359
Summary:
When solving a TypeVar from a union of implicit literals (e.g.,
Literal["a"] | Literal["b"] from tuple element types), skip literal
promotion. Previously, enumerate(("a", "b")) would promote the literals
to str when solving enumerate[T], losing type information.
The fix: when the type being checked against a TypeVar is a union where
all members are implicit literals, preserve them as-is instead of promoting
to the base type.
Fixes facebook#1323
Differential Revision: D97844612
727b93d to
d5a2dae
Compare
|
Diff from mypy_primer, showing the effect of this PR on open source code: vision (https://github.com/pytorch/vision)
+ ERROR torchvision/prototype/datasets/_builtin/imagenet.py:157:20-161:10: No matching overload found for function `dict.__init__` called with arguments: (dict[Literal['label', 'wnid'], Label | str], path=str, image=EncodedImage) [no-matching-overload]
+ ERROR torchvision/prototype/datasets/_builtin/mnist.py:402:22-409:10: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[Literal['digit_index', 'global_digit_index', 'nist_hsf_series', 'nist_label', 'nist_writer_id'], int]) [no-matching-overload]
+ ERROR torchvision/prototype/datasets/_builtin/mnist.py:410:22-102: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[Literal['duplicate', 'unused'], bool]) [no-matching-overload]
pandas (https://github.com/pandas-dev/pandas)
+ ERROR pandas/core/computation/ops.py:326:28-31: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[Literal['!=', '<', '<=', '==', '>', '>=', 'in', 'not in'], ((a: _SupportsComparison, b: _SupportsComparison, /) -> Any) | ((a: object, b: object, /) -> Any) | ((x: Unknown, y: Unknown) -> Unknown)] | dict[Literal['%', '*', '**', '+', '-', '/', '//'], ((a: Any, b: Any, /) -> Any)] | dict[Literal['&', 'and', 'or', '|'], ((a: Any, b: Any, /) -> Any)]) [no-matching-overload]
- ERROR pandas/io/html.py:967:35-39: Argument `str` is not assignable to parameter `flavor` with type `Literal['bs4', 'html5lib', 'lxml'] | None` in function `_parser_dispatch` [bad-argument-type]
+ ERROR pandas/io/html.py:967:35-39: Argument `Literal['bs4', 'lxml'] | str` is not assignable to parameter `flavor` with type `Literal['bs4', 'html5lib', 'lxml'] | None` in function `_parser_dispatch` [bad-argument-type]
core (https://github.com/home-assistant/core)
+ ERROR homeassistant/components/withings/sensor.py:691:12-18: Returned type `set[Literal['sleep', 'steps', 'weight']]` is not assignable to declared return type `set[str]` [bad-return]
parso (https://github.com/davidhalter/parso)
+ ERROR parso/python/errors.py:753:44-57: Argument `Literal['annotations']` is not assignable to parameter `object` with type `Literal['absolute_import', 'division', 'generator_stop', 'generators', 'nested_scopes', 'print_function', 'unicode_literals', 'with_statement']` in function `list.append` [bad-argument-type]
jax (https://github.com/google/jax)
+ ERROR jax/experimental/pallas/ops/gpu/ragged_dot_mgpu.py:299:23-29: `dict[Literal['block_k', 'block_m', 'block_n', 'grid_block_n', 'max_concurrent_steps'], int]` is not assignable to variable `best_kwargs` with type `dict[str, int]` [bad-assignment]
|
Primer Diff Classification❌ 4 regression(s) | ✅ 1 improvement(s) | 5 project(s) total | +8, -1 errors 4 regression(s) across vision, pandas, core, parso. error kinds:
Detailed analysis❌ Regression (4)vision (+3)
pandas (+2, -1)
ops.py line 326 (new html.py line 967 (changed error message): The old error reported The new
core (+1)
parso (+1)
All factual claims check out against the source code and Python type system behavior.
✅ Improvement (1)jax (+1)
Suggested fixesSummary: The PR's change to preserve literal unions through TypeVar solving causes false positives when literal-typed collections interact with invariant generic containers (dict, set, list) and their mutation methods (update, append, add). 1. In the
2. Alternative simpler fix: In the
3. Most conservative fix: In the
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (5 LLM) |
Summary:
When solving a TypeVar from a union of implicit literals (e.g.,
Literal["a"] | Literal["b"] from tuple element types), skip literal
promotion. Previously, enumerate(("a", "b")) would promote the literals
to str when solving enumerate[T], losing type information.
The fix: when the type being checked against a TypeVar is a union where
all members are implicit literals, preserve them as-is instead of promoting
to the base type.
Fixes #1323
Differential Revision: D97844612