Make tuple() constructor return structural Type::Tuple#2682
Make tuple() constructor return structural Type::Tuple#2682yangdanny97 wants to merge 1 commit intofacebook:mainfrom
tuple() constructor return structural Type::Tuple#2682Conversation
Summary:
Calling `tuple()` previously returned a nominal `Type::ClassType(tuple[T])`, which many downstream operations (concat, starred unpacking, except clause decomposition, etc.) didn't recognize because they only pattern-match on `Type::Tuple`. This forced at least 8 call sites to normalize via `as_tuple()`, and operations that didn't normalize were subtly broken.
Fix this at the source: post-process `construct_class` to convert the return type to `Type::Tuple(Tuple::Unbounded(T))` when the class is exactly `builtins.tuple`. This uses `is_builtin("tuple")` so it only fires for the exact builtin, not for NamedTuples or tuple subclasses, which correctly remain as nominal `ClassType`.
The existing `as_tuple()` normalizations downstream are kept because they still serve NamedTuples and tuple subclasses — they just become no-ops for plain `tuple()` calls.
Differential Revision: D95478317
|
@yangdanny97 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D95478317. |
|
Diff from mypy_primer, showing the effect of this PR on open source code: hydpy (https://github.com/hydpy-dev/hydpy)
- ERROR hydpy/models/manager/manager_derived.py:285:49-287:14: No matching overload found for function `networkx.utils.backends._dispatchable.__call__` called with arguments: (DiGraph[Node], nodelist=tuple[Node, ...], dtype=type[bool]) [no-matching-overload]
+ ERROR hydpy/models/manager/manager_derived.py:285:49-287:14: No matching overload found for function `networkx.utils.backends._dispatchable.__call__` called with arguments: (DiGraph[Node], nodelist=tuple[Node, *tuple[Any, ...]], dtype=type[bool]) [no-matching-overload]
bokeh (https://github.com/bokeh/bokeh)
- ERROR src/bokeh/plotting/_renderer.py:313:12-62: Returned type `tuple[str, ...] | tuple[str, None]` is not assignable to declared return type `tuple[str, str | None]` [bad-return]
+ ERROR src/bokeh/plotting/_renderer.py:313:12-62: Returned type `tuple[str, None] | tuple[str, ...]` is not assignable to declared return type `tuple[str, str | None]` [bad-return]
dedupe (https://github.com/dedupeio/dedupe)
- ERROR dedupe/api.py:998:9-31: Overload return type `Iterable[tuple[int, tuple[tuple[int, float], ...]]]` is not assignable to implementation return type `Generator[tuple[Unknown, tuple[tuple[RecordID, float], ...]] | tuple[Unknown, tuple[()]], Unknown]` [inconsistent-overload]
+ ERROR dedupe/api.py:998:9-31: Overload return type `Iterable[tuple[int, tuple[tuple[int, float], ...]]]` is not assignable to implementation return type `Generator[tuple[Unknown, tuple[()]] | tuple[Unknown, tuple[tuple[RecordID, float], ...]], Unknown]` [inconsistent-overload]
- ERROR dedupe/api.py:1003:9-31: Overload return type `Iterable[tuple[str, tuple[tuple[str, float], ...]]]` is not assignable to implementation return type `Generator[tuple[Unknown, tuple[tuple[RecordID, float], ...]] | tuple[Unknown, tuple[()]], Unknown]` [inconsistent-overload]
+ ERROR dedupe/api.py:1003:9-31: Overload return type `Iterable[tuple[str, tuple[tuple[str, float], ...]]]` is not assignable to implementation return type `Generator[tuple[Unknown, tuple[()]] | tuple[Unknown, tuple[tuple[RecordID, float], ...]], Unknown]` [inconsistent-overload]
pydantic (https://github.com/pydantic/pydantic)
- ERROR pydantic/v1/generics.py:124:25-132:14: No matching overload found for function `pydantic.v1.main.create_model` called with arguments: (str, __module__=str, __base__=tuple[type[GenericModelT], ...], __config__=None, __validators__=dict[str, classmethod[Any, Ellipsis, Any] | classmethod[Any, Ellipsis, Unknown]], __cls_kwargs__=None, **dict[Unknown, tuple[DeferredType, FieldInfo]]) [no-matching-overload]
+ ERROR pydantic/v1/generics.py:124:25-132:14: No matching overload found for function `pydantic.v1.main.create_model` called with arguments: (str, __module__=str, __base__=tuple[type[GenericModelT], *tuple[type[Any], ...]], __config__=None, __validators__=dict[str, classmethod[Any, Ellipsis, Any] | classmethod[Any, Ellipsis, Unknown]], __cls_kwargs__=None, **dict[Unknown, tuple[DeferredType, FieldInfo]]) [no-matching-overload]
vision (https://github.com/pytorch/vision)
- ERROR torchvision/ops/poolers.py:283:34-45: Argument `int | list[int] | tuple[int]` is not assignable to parameter `iterable` with type `Iterable[Unknown]` in function `tuple.__new__` [bad-argument-type]
- ERROR torchvision/transforms/_functional_pil.py:172:19-29: `int | object` is not assignable to variable `padding` with type `int | list[int] | tuple[int, ...]` [bad-assignment]
- ERROR torchvision/transforms/_functional_pil.py:181:49-56: Argument `int | list[int] | tuple[int, ...]` is not assignable to parameter `border` with type `int | tuple[int, ...]` in function `PIL.ImageOps.expand` [bad-argument-type]
- ERROR torchvision/transforms/_functional_pil.py:185:44-51: Argument `int | list[int] | tuple[int, ...]` is not assignable to parameter `border` with type `int | tuple[int, ...]` in function `PIL.ImageOps.expand` [bad-argument-type]
- ERROR torchvision/transforms/_functional_pil.py:199:31-37: No matching overload found for function `numpy._typing._ufunc._UFunc_Nin2_Nout1.__call__` called with arguments: (list[int | object], Literal[0]) [no-matching-overload]
- ERROR torchvision/transforms/_functional_pil.py:205:62-68: No matching overload found for function `numpy._typing._ufunc._UFunc_Nin2_Nout1.__call__` called with arguments: (list[int | object], Literal[0]) [no-matching-overload]
- ERROR torchvision/transforms/_functional_pil.py:210:25-97: No matching overload found for function `numpy.lib._arraypad_impl.pad` called with arguments: (Image, tuple[tuple[Unknown, Unknown], tuple[Unknown, Unknown]], mode=Literal['edge', 'reflect', 'symmetric']) [no-matching-overload]
+ ERROR torchvision/transforms/_functional_pil.py:210:25-97: No matching overload found for function `numpy.lib._arraypad_impl.pad` called with arguments: (Image, tuple[tuple[Any, Any], tuple[Any, Any]], mode=Literal['edge', 'reflect', 'symmetric']) [no-matching-overload]
- ERROR torchvision/transforms/_functional_pil.py:218:25-100: No matching overload found for function `numpy.lib._arraypad_impl.pad` called with arguments: (Image, tuple[tuple[Unknown, Unknown], tuple[Unknown, Unknown], tuple[Literal[0], Literal[0]]], Literal['edge', 'reflect', 'symmetric']) [no-matching-overload]
+ ERROR torchvision/transforms/_functional_pil.py:218:25-100: No matching overload found for function `numpy.lib._arraypad_impl.pad` called with arguments: (Image, tuple[tuple[Any, Any], tuple[Any, Any], tuple[Literal[0], Literal[0]]], Literal['edge', 'reflect', 'symmetric']) [no-matching-overload]
- ERROR torchvision/transforms/_functional_pil.py:221:25-92: No matching overload found for function `numpy.lib._arraypad_impl.pad` called with arguments: (Image | Unknown, tuple[tuple[Unknown, Unknown], tuple[Unknown, Unknown]], Literal['edge', 'reflect', 'symmetric']) [no-matching-overload]
+ ERROR torchvision/transforms/_functional_pil.py:221:25-92: No matching overload found for function `numpy.lib._arraypad_impl.pad` called with arguments: (Image | Unknown, tuple[tuple[Any, Any], tuple[Any, Any]], Literal['edge', 'reflect', 'symmetric']) [no-matching-overload]
setuptools (https://github.com/pypa/setuptools)
- ERROR setuptools/_distutils/_modified.py:61:12-59: Returned type `tuple[list[Unknown], ...] | tuple[list[_SourcesT], list[_TargetsT]]` is not assignable to declared return type `tuple[list[_SourcesT], list[_TargetsT]]` [bad-return]
+ ERROR setuptools/_distutils/_modified.py:61:12-59: Returned type `tuple[list[_SourcesT], list[_TargetsT]] | tuple[list[Unknown], ...]` is not assignable to declared return type `tuple[list[_SourcesT], list[_TargetsT]]` [bad-return]
jax (https://github.com/google/jax)
+ ERROR jax/experimental/mosaic/gpu/fragmented_array.py:2434:20-84: Expected an iterable, got `tuple[Literal[False], ...] | tuple[Literal[True] | Unknown, ...]` [not-iterable]
mypy (https://github.com/python/mypy)
- ERROR mypy/modulefinder.py:40:37-67: No matching overload found for function `map.__new__` called with arguments: (type[map[Unknown]], Overload[
- [AnyStr: (str, bytes)](path: PathLike[AnyStr]) -> AnyStr
- [AnyStr: (str, bytes)](path: AnyStr) -> AnyStr
- ], tuple[str, ...]) [no-matching-overload]
- [AnyStr: (str, bytes)](path: PathLike[AnyStr]) -> AnyStr
- [AnyStr: (str, bytes)](path: AnyStr) -> AnyStr
- ], tuple[str, ...]) [no-matching-overload]
- ERROR mypy/modulefinder.py:42:35-63: No matching overload found for function `map.__new__` called with arguments: (type[map[Unknown]], Overload[
- ERROR mypy/modulefinder.py:44:38-69: No matching overload found for function `map.__new__` called with arguments: (type[map[Unknown]], Overload[
- [AnyStr: (str, bytes)](path: PathLike[AnyStr]) -> AnyStr
- [AnyStr: (str, bytes)](path: AnyStr) -> AnyStr
- ], tuple[str, ...]) [no-matching-overload]
- [AnyStr: (str, bytes)](path: PathLike[AnyStr]) -> AnyStr
- [AnyStr: (str, bytes)](path: AnyStr) -> AnyStr
- ], tuple[str, ...]) [no-matching-overload]
- ERROR mypy/modulefinder.py:46:39-71: No matching overload found for function `map.__new__` called with arguments: (type[map[Unknown]], Overload[
|
Primer Diff Classification❌ 1 regression(s) | ➖ 6 neutral | 7 project(s) total 1 regression(s) across jax. error kinds:
Detailed analysis❌ Regression (1)jax (+1)
➖ Neutral (6)hydpy (+1, -1)
bokeh (+1, -1)
dedupe (+2, -2)
pydantic (+1, -1)
vision (+3, -9)
setuptools (+1, -1)
Suggested FixSummary: The PR changed tuple() constructor calls to return structural Type::Tuple instead of nominal ClassType(tuple[T]), which broke union type iterable checking in jax. 1. In
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (5 heuristic, 2 LLM) |
|
This pull request has been merged in 13849da. |
Summary:
Calling
tuple()previously returned a nominalType::ClassType(tuple[T]), which many downstream operations (concat, starred unpacking, except clause decomposition, etc.) didn't recognize because they only pattern-match onType::Tuple. This forced at least 8 call sites to normalize viaas_tuple(), and operations that didn't normalize were subtly broken.Fix this at the source: post-process
construct_classto convert the return type toType::Tuple(Tuple::Unbounded(T))when the class is exactlybuiltins.tuple. This usesis_builtin("tuple")so it only fires for the exact builtin, not for NamedTuples or tuple subclasses, which correctly remain as nominalClassType.The existing
as_tuple()normalizations downstream are kept because they still serve NamedTuples and tuple subclasses — they just become no-ops for plaintuple()calls.Differential Revision: D95478317