Skip to content

Reject an overload that violates a restriction under a gradual expected type (#5240) - #5240

Closed
kinto0 wants to merge 2 commits into
facebook:mainfrom
kinto0:export-D125293977
Closed

kinto0 wants to merge 2 commits into
facebook:mainfrom
kinto0:export-D125293977

Conversation

@kinto0

@kinto0 kinto0 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

Stack context: Make a restriction violation under a gradual expected type reject the overload that caused it.

  • Problem: a constructor call with a gradual expected type, such as return pd.Series(pd.NA, dtype="Int64") under a bare -> pd.Series, reports bad-specialization. The same call without the annotation type checks clean. The trigger is an overloaded __new__ or __init__ where an earlier overload accepts the argument only through the class type parameter (data: S1 | ...), and a later overload accepts it directly (data: NAType).
  • Cause: since D115555748, the expected type solves S1 to Any, and argument matching records a restriction violation on that answer. The constructor reports the violation only when it finishes, so overload selection does not see it. The earlier overload matches, and the call reports the violation instead of choosing the later overload.
  • Fix: each call that matches arguments against a restricted answer now takes the violations that it caused into its own specialization errors. Overload selection rejects the overload and continues, as it does without the expected type.
  • Side effect: for a constructor without overloads, the violation is now a hard error of the hinted attempt, so construct_with_hint discards the expected type. The call then reports the same errors as the unhinted call. This matches the intent of D115555748: an Any from the expected type says nothing, and argument matching checks the call normally.

Reviewed By: rchen152

Differential Revision: D125293977

@meta-cla meta-cla Bot added the cla signed label Oct 14, 2026
@meta-codesync

meta-codesync Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

@kinto0 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D125293977.

@rchen152 rchen152 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review automatically exported from Phabricator review in Meta.

@kinto0 kinto0 added this to the v1.4 milestone Oct 14, 2026
@github-actions

This comment has been minimized.

…ded constructors

Summary:
Stack context: Fix overload selection errors that cause false positives in pandas-stubs.

- Add two bug-marked tests for constructor calls that violate a restriction under a gradual expected type. With overloads, `Series(NA(), dtype="Int64")` under a bare `-> Series` reports `bad-specialization`, but the unhinted call picks the `NA` overload and reports nothing. Without overloads, the hinted call hides a bad argument that the unhinted call reports.
- The first test is the pattern that `pd.Series(pd.NA, dtype="Int64")` hits with pandas-stubs since D115555748. The next diff fixes both tests.

Differential Revision: D125293976
@meta-codesync meta-codesync Bot changed the title Reject an overload that violates a restriction under a gradual expected type Reject an overload that violates a restriction under a gradual expected type (#5240) Oct 14, 2026
kinto0 added a commit to kinto0/pyrefly that referenced this pull request Oct 14, 2026
…ed type (facebook#5240)

Summary:

Stack context: Make a restriction violation under a gradual expected type reject the overload that caused it.

- Problem: a constructor call with a gradual expected type, such as `return pd.Series(pd.NA, dtype="Int64")` under a bare `-> pd.Series`, reports `bad-specialization`. The same call without the annotation type checks clean. The trigger is an overloaded `__new__` or `__init__` where an earlier overload accepts the argument only through the class type parameter (`data: S1 | ...`), and a later overload accepts it directly (`data: NAType`).
- Cause: since D115555748, the expected type solves `S1` to `Any`, and argument matching records a restriction violation on that answer. The constructor reports the violation only when it finishes, so overload selection does not see it. The earlier overload matches, and the call reports the violation instead of choosing the later overload.
- Fix: each call that matches arguments against a restricted answer now takes the violations that it caused into its own specialization errors. Overload selection rejects the overload and continues, as it does without the expected type.
- Side effect: for a constructor without overloads, the violation is now a hard error of the hinted attempt, so `construct_with_hint` discards the expected type. The call then reports the same errors as the unhinted call. This matches the intent of D115555748: an `Any` from the expected type says nothing, and argument matching checks the call normally.

Reviewed By: rchen152

Differential Revision: D125293977
@kinto0
kinto0 force-pushed the export-D125293977 branch from 9ba0fbb to 53faf85 Compare October 14, 2026 21:33
@github-actions github-actions Bot added size/m and removed size/m labels Oct 14, 2026
…ed type (facebook#5240)

Summary:
Pull Request resolved: facebook#5240

Stack context: Make a restriction violation under a gradual expected type reject the overload that caused it.

- Problem: a constructor call with a gradual expected type, such as `return pd.Series(pd.NA, dtype="Int64")` under a bare `-> pd.Series`, reports `bad-specialization`. The same call without the annotation type checks clean. The trigger is an overloaded `__new__` or `__init__` where an earlier overload accepts the argument only through the class type parameter (`data: S1 | ...`), and a later overload accepts it directly (`data: NAType`).
- Cause: since D115555748, the expected type solves `S1` to `Any`, and argument matching records a restriction violation on that answer. The constructor reports the violation only when it finishes, so overload selection does not see it. The earlier overload matches, and the call reports the violation instead of choosing the later overload.
- Fix: each call that matches arguments against a restricted answer now takes the violations that it caused into its own specialization errors. Overload selection rejects the overload and continues, as it does without the expected type.
- Side effect: for a constructor without overloads, the violation is now a hard error of the hinted attempt, so `construct_with_hint` discards the expected type. The call then reports the same errors as the unhinted call. This matches the intent of D115555748: an `Any` from the expected type says nothing, and argument matching checks the call normally.

Reviewed By: rchen152

Differential Revision: D125293977
@kinto0
kinto0 force-pushed the export-D125293977 branch from 53faf85 to 7f4c935 Compare October 14, 2026 21:36
@github-actions github-actions Bot added size/m and removed size/m labels Oct 14, 2026
@github-actions

Copy link
Copy Markdown

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

============================================================
SUMMARY
============================================================
Total: +15 new errors, -31 fixed errors
By preset: +15/-29 (default), +15/-29 (strict)

Projects with changes (6):
  steam.py: +1 -1
  spark: +0 -14
  pandas-stubs: +0 -1
  pandas: +1 -0
  meson: +13 -13
  xarray: +0 -2
============================================================

FULL DIFF DETAILS
------------------------------------------------------------

steam.py (https://github.com/Gobot1234/steam.py)
- ERROR steam/abc.py:552:30-561:10: `PartialApp | tuple[str, int]` is not assignable to upper bound `PartialApp` of type variable `AppT` [bad-specialization]
+ ERROR steam/abc.py:552:30-561:10: `PartialApp[None] | tuple[str, int]` is not assignable to upper bound `PartialApp` of type variable `AppT` [bad-specialization]

spark (https://github.com/apache/spark)
- ERROR python/pyspark/eval_handlers/_pandas.py:812:47-56: `Unknown | None` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pandas/test_pandas_series_astype.py:99:44-72: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pandas/test_pandas_series_astype.py:100:44-79: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pandas/test_pandas_series_astype.py:101:51-84: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pandas/test_pandas_series_astype.py:102:43-74: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pandas/test_pandas_series_astype.py:103:43-96: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pandas/test_pandas_series_astype.py:104:41-86: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:165:54-94: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:171:47-75: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:242:47-67: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:243:54-245:10: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:246:48-101: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:359:57-361:10: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py:362:57-364:10: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
- ERROR tests/test_resampler.py:306:22-27: `DatetimeIndexResampler[Series]` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]

pandas (https://github.com/pandas-dev/pandas)
+ ERROR pandas/core/arrays/interval.py:699:35-40: Argument `DatetimeArray | NaTType | Period | Timedelta | TimedeltaArray | Timestamp | ndarray` is not assignable to parameter `right` with type `NaTType | Period | Timedelta | Timestamp` in function `pandas._libs.interval.Interval.__init__` [bad-argument-type]

meson (https://github.com/mesonbuild/meson)
+ ERROR mesonbuild/mparser.py:859:36-88: `+` is not supported between `str` and `None` [unsupported-operation]
- ERROR mesonbuild/mparser.py:859:27-32: The type of attribute `value` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR mesonbuild/mparser.py:859:83-88: The type of attribute `value` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR unittests/versiontests.py:302:43-63: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:303:43-64: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:307:43-84: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:310:44-65: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:311:44-64: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:314:45-65: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:315:45-65: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:318:45-66: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:319:45-67: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:322:49-69: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
- ERROR unittests/versiontests.py:327:44-64: `int` is not assignable to upper bound `Comparable` of type variable `_V` [bad-specialization]
+ ERROR unittests/versiontests.py:302:48-49: Argument `Literal[3]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:303:48-50: Argument `Literal[12]` is not assignable to parameter `max` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:307:48-49: Argument `Literal[5]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:307:68-70: Argument `Literal[10]` is not assignable to parameter `max` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:310:49-51: Argument `Literal[11]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:311:49-50: Argument `Literal[4]` is not assignable to parameter `max` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:314:50-51: Argument `Literal[7]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:315:50-51: Argument `Literal[8]` is not assignable to parameter `max` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:318:50-51: Argument `Literal[5]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:319:50-52: Argument `Literal[10]` is not assignable to parameter `max` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:322:54-55: Argument `Literal[5]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]
+ ERROR unittests/versiontests.py:327:49-50: Argument `Literal[5]` is not assignable to parameter `min` with type `@_ | None` in function `mesonbuild.utils.universal.Range.__init__` [bad-argument-type]

xarray (https://github.com/pydata/xarray)
- ERROR xarray/tests/test_pandas_to_xarray.py:56:20-70: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
- ERROR xarray/tests/test_pandas_to_xarray.py:73:25-58: `object` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]

@meta-codesync meta-codesync Bot closed this in ea8421d Oct 14, 2026
@meta-codesync meta-codesync Bot added the Merged label Oct 14, 2026
@meta-codesync

meta-codesync Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

This pull request has been merged in ea8421d.

@kinto0
kinto0 deleted the export-D125293977 branch October 15, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants