Skip to content

fix Incorrect overload selection for itertools.accumulate #2876#2947

Closed
asukaminato0721 wants to merge 2 commits intofacebook:mainfrom
asukaminato0721:2876
Closed

fix Incorrect overload selection for itertools.accumulate #2876#2947
asukaminato0721 wants to merge 2 commits intofacebook:mainfrom
asukaminato0721:2876

Conversation

@asukaminato0721
Copy link
Copy Markdown
Contributor

@asukaminato0721 asukaminato0721 commented Mar 27, 2026

Summary

Fixes #2876

when matching a generic callable target that still has inference vars, Pyrefly now checks the return type before the parameter list

Test Plan

add test

@meta-cla meta-cla Bot added the cla signed label Mar 27, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 27, 2026 14:37
Copilot AI review requested due to automatic review settings March 27, 2026 14:37
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes pyrefly’s overload selection so that failed tentative overload candidates don’t leave behind solver state that biases later candidates (e.g., "{}/{}".format used with itertools.accumulate), and adjusts callable subtyping to avoid prematurely pinning inferred vars when the target callable still contains unsolved vars.

Changes:

  • Add solver snapshot/restore support and use it to roll back state after failed tentative overload checks.
  • Update callable subtyping to compare return types before parameters when the target callable contains inferred vars.
  • Add a regression test covering itertools.accumulate with a bound str.format.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
pyrefly/lib/test/overload.rs Adds regression test for bound-method overload behavior via itertools.accumulate.
pyrefly/lib/solver/subset.rs Wraps overload candidate checks with rollback and adjusts callable subtyping comparison order when vars are present.
pyrefly/lib/solver/solver.rs Introduces solver snapshot/restore (variables + instantiation errors) to support rollback.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyrefly/lib/solver/subset.rs Outdated
Comment thread pyrefly/lib/solver/solver.rs Outdated
Comment thread pyrefly/lib/solver/solver.rs Outdated
Comment on lines +1515 to +1517
let want_has_vars =
Type::Callable(Box::new(u.clone())).may_contain_quantified_var();
if want_has_vars {
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

want_has_vars currently builds a fresh Type::Callable(Box::new(u.clone())) just to call may_contain_quantified_var(), which allocates and clones the full callable signature. If this check is on a hot path, consider a cheaper predicate (e.g., a helper that inspects u.params/u.ret directly, or adding a Callable::may_contain_quantified_var() method) to avoid the extra clone/allocation.

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown

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

openlibrary (https://github.com/internetarchive/openlibrary)
- ERROR openlibrary/core/imports.py:139:24-34: Argument `type[ImportItem]` is not assignable to parameter `func` with type `(@_) -> @_` in function `map.__new__` [bad-argument-type]
+ ERROR openlibrary/core/imports.py:139:24-34: Argument `type[ImportItem]` is not assignable to parameter `func` with type `(@_) -> ImportItem` in function `map.__new__` [bad-argument-type]

antidote (https://github.com/Finistere/antidote)
- ERROR tests/core/test_inject.py:549:13-27: Argument `() -> object` is not assignable to parameter `__arg` with type `(Any, ParamSpec(@_)) -> @_` in function `antidote.core.Inject.method` [bad-argument-type]
+ ERROR tests/core/test_inject.py:549:13-27: Argument `() -> object` is not assignable to parameter `__arg` with type `(Any, ParamSpec(@_)) -> object` in function `antidote.core.Inject.method` [bad-argument-type]

egglog-python (https://github.com/egraphs-good/egglog-python)
- ERROR python/egglog/thunk.py:50:20-49: Argument `Unresolved[T, *TS]` is not assignable to parameter `state` with type `Error | Resolved[@_] | Resolving | Unresolved[@_, *tuple[object, ...]]` in function `Thunk.__init__` [bad-argument-type]
+ ERROR python/egglog/thunk.py:50:20-49: Argument `Unresolved[T, *TS]` is not assignable to parameter `state` with type `Error | Resolved[T] | Resolving | Unresolved[T, *tuple[object, ...]]` in function `Thunk.__init__` [bad-argument-type]

anyio (https://github.com/agronholm/anyio)
- ERROR src/anyio/_core/_fileio.py:568:13-30: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> @_` in function `anyio.to_thread.run_sync` [bad-argument-type]
+ ERROR src/anyio/_core/_fileio.py:568:13-30: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> bool` in function `anyio.to_thread.run_sync` [bad-argument-type]
- ERROR src/anyio/_core/_fileio.py:615:13-29: Argument `(self: Path, *, follow_symlinks: bool = True) -> str` is not assignable to parameter `func` with type `(**tuple[*@_]) -> @_` in function `anyio.to_thread.run_sync` [bad-argument-type]
+ ERROR src/anyio/_core/_fileio.py:615:13-29: Argument `(self: Path, *, follow_symlinks: bool = True) -> str` is not assignable to parameter `func` with type `(**tuple[*@_]) -> str` in function `anyio.to_thread.run_sync` [bad-argument-type]
- ERROR src/anyio/_core/_fileio.py:646:13-30: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> @_` in function `anyio.to_thread.run_sync` [bad-argument-type]
+ ERROR src/anyio/_core/_fileio.py:646:13-30: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> bool` in function `anyio.to_thread.run_sync` [bad-argument-type]
- ERROR src/anyio/_core/_fileio.py:656:13-31: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> @_` in function `anyio.to_thread.run_sync` [bad-argument-type]
+ ERROR src/anyio/_core/_fileio.py:656:13-31: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> bool` in function `anyio.to_thread.run_sync` [bad-argument-type]
- ERROR src/anyio/_core/_fileio.py:756:13-29: Argument `(self: Path, *, follow_symlinks: bool = True) -> str` is not assignable to parameter `func` with type `(**tuple[*@_]) -> @_` in function `anyio.to_thread.run_sync` [bad-argument-type]
+ ERROR src/anyio/_core/_fileio.py:756:13-29: Argument `(self: Path, *, follow_symlinks: bool = True) -> str` is not assignable to parameter `func` with type `(**tuple[*@_]) -> str` in function `anyio.to_thread.run_sync` [bad-argument-type]

Expression (https://github.com/cognitedata/Expression)
- ERROR tests/test_array.py:47:36-50: Argument `(TypedArray[object]) -> TypedArray[str]` is not assignable to parameter `fn1` with type `(TypedArray[int]) -> @_` in function `expression.core.pipe.pipe` [bad-argument-type]
+ ERROR tests/test_array.py:47:36-50: Argument `(TypedArray[object]) -> TypedArray[str]` is not assignable to parameter `fn1` with type `(TypedArray[int]) -> TypedArray[str]` in function `expression.core.pipe.pipe` [bad-argument-type]

freqtrade (https://github.com/freqtrade/freqtrade)
+ ERROR freqtrade/data/metrics.py:333:12-40: Returned type `tuple[Series[float] | Series | float | Unknown, Series[float] | Series | float | Unknown]` is not assignable to declared return type `tuple[float, float]` [bad-return]

more-itertools (https://github.com/more-itertools/more-itertools)
+ ERROR more_itertools/recipes.py:1121:34-52: Argument `Iterator[int]` is not assignable to parameter `iter2` with type `Iterable[Literal[0]]` in function `map.__new__` [bad-argument-type]

setuptools (https://github.com/pypa/setuptools)
+ ERROR setuptools/_vendor/more_itertools/recipes.py:1174:34-52: Argument `Iterator[int]` is not assignable to parameter `iter2` with type `Iterable[Literal[0]]` in function `map.__new__` [bad-argument-type]

pwndbg (https://github.com/pwndbg/pwndbg)
- ERROR pwndbg/commands/onegadget.py:27:1-63: Argument `(((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) -> (show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) | ((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None)` is not assignable to parameter with type `((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) -> (show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None` [bad-argument-type]
+ ERROR pwndbg/commands/onegadget.py:27:1-63: Argument `(((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) -> (show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) | ((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None)` is not assignable to parameter with type `((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) -> ((show_unsat: bool = False, no_unknown: bool = False, verbose: bool = False) -> None) | None` [bad-argument-type]

schemathesis (https://github.com/schemathesis/schemathesis)
- ERROR src/schemathesis/specs/openapi/adapter/parameters.py:1175:45-60: Argument `(value: dict[str, Any]) -> dict[str, Any]` is not assignable to parameter `pack` with type `(GeneratedValue) -> @_` in function `hypothesis.strategies._internal.strategies.SearchStrategy.map` [bad-argument-type]
+ ERROR src/schemathesis/specs/openapi/adapter/parameters.py:1175:45-60: Argument `(value: dict[str, Any]) -> dict[str, Any]` is not assignable to parameter `pack` with type `(GeneratedValue) -> dict[str, Any]` in function `hypothesis.strategies._internal.strategies.SearchStrategy.map` [bad-argument-type]
- ERROR src/schemathesis/specs/openapi/adapter/parameters.py:1188:45-74: Argument `(value: dict[str, Any]) -> dict[str, Any]` is not assignable to parameter `pack` with type `(GeneratedValue) -> @_` in function `hypothesis.strategies._internal.strategies.SearchStrategy.map` [bad-argument-type]
+ ERROR src/schemathesis/specs/openapi/adapter/parameters.py:1188:45-74: Argument `(value: dict[str, Any]) -> dict[str, Any]` is not assignable to parameter `pack` with type `(GeneratedValue) -> dict[str, Any]` in function `hypothesis.strategies._internal.strategies.SearchStrategy.map` [bad-argument-type]

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
+ ERROR tests/series/test_agg.py:26:22-44: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:27:22-46: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:28:22-43: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:29:22-43: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:34:22-44: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:35:22-46: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:36:22-43: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:37:22-43: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:42:22-44: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:43:22-46: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:44:22-43: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:45:22-43: assert_type(Unknown, float) failed [assert-type]
+ ERROR tests/series/test_agg.py:52:22-46: assert_type(Unknown, complex) failed [assert-type]
+ ERROR tests/series/test_agg.py:102:22-51: assert_type(Unknown, Timedelta) failed [assert-type]
+ ERROR tests/series/test_agg.py:103:22-53: assert_type(Unknown, Timedelta) failed [assert-type]
+ ERROR tests/series/test_agg.py:104:22-50: assert_type(Unknown, Timedelta) failed [assert-type]

bandersnatch (https://github.com/pypa/bandersnatch)
- ERROR src/bandersnatch/mirror.py:814:70-81: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> @_` in function `asyncio.events.AbstractEventLoop.run_in_executor` [bad-argument-type]
+ ERROR src/bandersnatch/mirror.py:814:70-81: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `func` with type `(**tuple[*@_]) -> bool` in function `asyncio.events.AbstractEventLoop.run_in_executor` [bad-argument-type]

pytest-robotframework (https://github.com/detachhead/pytest-robotframework)
- ERROR tests/type_tests.py:64:5-40: Argument `() -> None` is not assignable to parameter `fn` with type `() -> AbstractContextManager[@_]` in function `pytest_robotframework._WrappedContextManagerKeywordDecorator.__call__` [bad-argument-type]
+ ERROR tests/type_tests.py:64:5-40: Argument `() -> None` is not assignable to parameter `fn` with type `(ParamSpec(@_)) -> AbstractContextManager[@_]` in function `pytest_robotframework._WrappedContextManagerKeywordDecorator.__call__` [bad-argument-type]

mypy (https://github.com/python/mypy)
- ERROR mypy/typeshed/stdlib/builtins.pyi:227:5-17: Argument `(metacls: Self@type, name: str, bases: tuple[type[Any], ...], /, **kwds: Any) -> MutableMapping[str, object]` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:227:5-17: Argument `(metacls: Self@type, name: str, bases: tuple[type[Any], ...], /, **kwds: Any) -> MutableMapping[str, object]` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> MutableMapping[str, object]` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:277:9-21: Argument `(cls: Self@int, bytes: Buffer | Iterable[SupportsIndex] | SupportsBytes, byteorder: Literal['big', 'little'] = 'big', *, signed: bool = False) -> Self@int` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:277:9-21: Argument `(cls: Self@int, bytes: Buffer | Iterable[SupportsIndex] | SupportsBytes, byteorder: Literal['big', 'little'] = 'big', *, signed: bool = False) -> Self@int` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> Self@int` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:370:5-17: Argument `(cls: Self@float, string: str, /) -> Self@float` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:370:5-17: Argument `(cls: Self@float, string: str, /) -> Self@float` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> Self@float` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:640:9-21: Argument `(cls: Self@bytes, string: str, /) -> Self@bytes` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:640:9-21: Argument `(cls: Self@bytes, string: str, /) -> Self@bytes` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> Self@bytes` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:750:9-21: Argument `(cls: Self@bytearray, string: str, /) -> Self@bytearray` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:750:9-21: Argument `(cls: Self@bytearray, string: str, /) -> Self@bytearray` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> Self@bytearray` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:1117:5-17: Argument `(cls: Self@mypy.typeshed.stdlib.builtins.dict, iterable: Iterable[_T], value: None = None, /) -> builtins.dict[_T, Any | None]` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:1117:5-17: Argument `(cls: Self@mypy.typeshed.stdlib.builtins.dict, iterable: Iterable[_T], value: None = None, /) -> builtins.dict[_T, Any | None]` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> builtins.dict[_T, Any | None]` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:1119:9-17: `fromkeys` has type `classmethod[Unknown, Ellipsis, Unknown]` after decorator application, which is not callable [invalid-overload]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:1119:9-17: `fromkeys` has type `classmethod[Unknown, Ellipsis, dict[_T, Any | None]]` after decorator application, which is not callable [invalid-overload]
- ERROR mypy/typeshed/stdlib/builtins.pyi:1120:5-17: Argument `(cls: Self@mypy.typeshed.stdlib.builtins.dict, iterable: Iterable[_T], value: _S, /) -> builtins.dict[_T, _S]` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> @_` in function `classmethod.__init__` [bad-argument-type]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:1120:5-17: Argument `(cls: Self@mypy.typeshed.stdlib.builtins.dict, iterable: Iterable[_T], value: _S, /) -> builtins.dict[_T, _S]` is not assignable to parameter `f` with type `(type[@_], ParamSpec(@_)) -> builtins.dict[_T, _S]` in function `classmethod.__init__` [bad-argument-type]
- ERROR mypy/typeshed/stdlib/builtins.pyi:1122:9-17: `fromkeys` has type `classmethod[@_, @_, @_]` after decorator application, which is not callable [invalid-overload]
+ ERROR mypy/typeshed/stdlib/builtins.pyi:1122:9-17: `fromkeys` has type `classmethod[@_, @_, dict[_T, _S]]` after decorator application, which is not callable [invalid-overload]

werkzeug (https://github.com/pallets/werkzeug)
- ERROR tests/test_utils.py:285:5-27: Argument `() -> Literal[42]` is not assignable to parameter `fget` with type `(Any) -> @_` in function `werkzeug.utils.cached_property.__init__` [bad-argument-type]
+ ERROR tests/test_utils.py:285:5-27: Argument `() -> Literal[42]` is not assignable to parameter `fget` with type `(Any) -> int` in function `werkzeug.utils.cached_property.__init__` [bad-argument-type]

core (https://github.com/home-assistant/core)
- ERROR homeassistant/components/backup/backup.py:101:56-74: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `target` with type `(**tuple[*@_]) -> @_` in function `homeassistant.core.HomeAssistant.async_add_executor_job` [bad-argument-type]
+ ERROR homeassistant/components/backup/backup.py:101:56-74: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `target` with type `(**tuple[*@_]) -> bool` in function `homeassistant.core.HomeAssistant.async_add_executor_job` [bad-argument-type]
- ERROR homeassistant/components/image_upload/__init__.py:223:54-73: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `target` with type `(**tuple[*@_]) -> @_` in function `homeassistant.core.HomeAssistant.async_add_executor_job` [bad-argument-type]
+ ERROR homeassistant/components/image_upload/__init__.py:223:54-73: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `target` with type `(**tuple[*@_]) -> bool` in function `homeassistant.core.HomeAssistant.async_add_executor_job` [bad-argument-type]
- ERROR homeassistant/components/media_source/local_source.py:305:49-67: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `target` with type `(**tuple[*@_]) -> @_` in function `homeassistant.core.HomeAssistant.async_add_executor_job` [bad-argument-type]
+ ERROR homeassistant/components/media_source/local_source.py:305:49-67: Argument `(self: Path, *, follow_symlinks: bool = True) -> bool` is not assignable to parameter `target` with type `(**tuple[*@_]) -> bool` in function `homeassistant.core.HomeAssistant.async_add_executor_job` [bad-argument-type]

xarray (https://github.com/pydata/xarray)
+ ERROR ci/numpydoc-public-api.py:116:20-79: Argument `Generator[set[str]]` is not assignable to parameter `iterable` with type `Iterable[set[Unknown]]` in function `functools.reduce` [bad-argument-type]

spark (https://github.com/apache/spark)
- ERROR python/pyspark/pandas/tests/series/test_cumulative.py:31:41-43: Argument `Series[float | None]` is not assignable to parameter `self` with type `SupportsGetItem[Scalar, _SupportsAdd[float]]` in function `pandas.core.series.Series.sum` [bad-argument-type]
+ ERROR python/pyspark/pandas/tests/series/test_cumulative.py:31:41-43: Argument `Series[float | None]` is not assignable to parameter `self` with type `SupportsGetItem[Scalar, _SupportsAdd[@_]]` in function `pandas.core.series.Series.sum` [bad-argument-type]
- ERROR python/pyspark/pandas/tests/series/test_cumulative.py:44:41-43: Argument `Series[float | None]` is not assignable to parameter `self` with type `SupportsGetItem[Scalar, _SupportsAdd[float]]` in function `pandas.core.series.Series.sum` [bad-argument-type]
+ ERROR python/pyspark/pandas/tests/series/test_cumulative.py:44:41-43: Argument `Series[float | None]` is not assignable to parameter `self` with type `SupportsGetItem[Scalar, _SupportsAdd[@_]]` in function `pandas.core.series.Series.sum` [bad-argument-type]
- ERROR python/pyspark/pandas/tests/series/test_cumulative.py:57:41-43: Argument `Series[float | None]` is not assignable to parameter `self` with type `SupportsGetItem[Scalar, _SupportsAdd[float]]` in function `pandas.core.series.Series.sum` [bad-argument-type]
+ ERROR python/pyspark/pandas/tests/series/test_cumulative.py:57:41-43: Argument `Series[float | None]` is not assignable to parameter `self` with type `SupportsGetItem[Scalar, _SupportsAdd[@_]]` in function `pandas.core.series.Series.sum` [bad-argument-type]

attrs (https://github.com/python-attrs/attrs)
- ERROR tests/test_converters.py:25:23-26: Argument `type[int]` is not assignable to parameter `converter` with type `(ConvertibleToInt, AttrsInstance, Attribute[Unknown]) -> @_` in function `attr.Converter.__init__` [bad-argument-type]
+ ERROR tests/test_converters.py:25:23-26: Argument `type[int]` is not assignable to parameter `converter` with type `(@_, AttrsInstance, Attribute[Unknown]) -> @_` in function `attr.Converter.__init__` [bad-argument-type]
- ERROR tests/test_converters.py:104:23-30: Argument `(_: Unknown, __: Unknown, ___: Unknown) -> float` is not assignable to parameter `converter` with type `(Unknown) -> @_` in function `attr.Converter.__init__` [bad-argument-type]
+ ERROR tests/test_converters.py:104:23-30: Argument `(_: Unknown, __: Unknown, ___: Unknown) -> float` is not assignable to parameter `converter` with type `(Unknown) -> float` in function `attr.Converter.__init__` [bad-argument-type]

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/prefect/server/database/_migrations/env.py:190:35-52: Argument `(connection: AsyncEngine) -> None` is not assignable to parameter `fn` with type `(Connection, ParamSpec(@_)) -> @_` in function `sqlalchemy.ext.asyncio.engine.AsyncConnection.run_sync` [bad-argument-type]
+ ERROR src/prefect/server/database/_migrations/env.py:190:35-52: Argument `(connection: AsyncEngine) -> None` is not assignable to parameter `fn` with type `(Connection, ParamSpec(@_)) -> None` in function `sqlalchemy.ext.asyncio.engine.AsyncConnection.run_sync` [bad-argument-type]

@github-actions
Copy link
Copy Markdown

Primer Diff Classification

❌ 7 regression(s) | ➖ 13 neutral | 20 project(s) total | +53, -33 errors

7 regression(s) across freqtrade, more-itertools, setuptools, pandas-stubs, pytest-robotframework, xarray, spark. error kinds: bad-argument-type, bad-return, assert-type failures on pandas Series aggregation methods. caused by is_subset_overload_candidate().

Project Verdict Changes Error Kinds Root Cause
openlibrary ➖ Neutral +1, -1 map() constructor callable inference pyrefly/lib/solver/subset.rs
antidote ➖ Neutral +1, -1 Callable inference message change pyrefly/lib/solver/subset.rs
egglog-python ➖ Neutral +1, -1 bad-argument-type
anyio ➖ Neutral +5, -5 bad-argument-type is_subset_overload_candidate()
Expression ➖ Neutral +1, -1 bad-argument-type
freqtrade ❌ Regression +1 bad-return pyrefly/lib/solver/subset.rs
more-itertools ❌ Regression +1 bad-argument-type is_subset_overload_candidate()
setuptools ❌ Regression +1 bad-argument-type is_subset_overload_candidate()
pwndbg ➖ Neutral +1, -1 bad-argument-type
schemathesis ➖ Neutral +2, -2 bad-argument-type
pandas-stubs ❌ Regression +16 assert-type failures on pandas Series aggregation methods is_subset_overload_candidate()
bandersnatch ➖ Neutral +1, -1 bad-argument-type pyrefly/lib/solver/subset.rs
pytest-robotframework ❌ Regression +1, -1 bad-argument-type pyrefly/lib/solver/subset.rs
mypy ➖ Neutral +9, -9 bad-argument-type on @classmethod pyrefly/lib/solver/subset.rs
werkzeug ➖ Neutral +1, -1 bad-argument-type
core ➖ Neutral +3, -3 Changed error messages for async_add_executor_job with Path methods is_subset_overload_candidate()
xarray ❌ Regression +1 bad-argument-type pyrefly/lib/solver/subset.rs
spark ❌ Regression +3, -3 New bad-argument-type errors with @_ inference failure pyrefly/lib/solver/subset.rs
attrs ➖ Neutral +2, -2 bad-argument-type pyrefly/lib/solver/subset.rs
prefect ➖ Neutral +1, -1 bad-argument-type pyrefly/lib/solver/subset.rs
Detailed analysis

❌ Regression (7)

freqtrade (+1)

Looking at the code: expectancy starts as float (0.0) and is potentially reassigned to (winrate * average_win) - (loserate * average_loss). Here winrate and loserate are int / int = float, and average_win/average_loss are either float / int = float or 0 (int). So expectancy should be float. Similarly expectancy_ratio starts as float (100.0) and may be reassigned to ((1 + risk_reward_ratio) * winrate) - 1 where risk_reward_ratio = average_win / average_loss is float. So expectancy_ratio should also be float.

The problem is that pyrefly is inferring profit_sum = winning_trades['profit_abs'].sum() as Series[float] | Series | float | Unknown instead of a scalar type. This is a pandas .sum() overload resolution issue — the PR's change to check return types before parameters during overload matching is causing pyrefly to select a broader overload for pandas operations. The Unknown in the type also suggests inference degradation.

Since the actual runtime types are clearly float, and neither mypy nor pyright flag this, this is a false positive introduced by the PR's overload resolution changes.

Attribution: The PR changes overload selection logic in pyrefly/lib/solver/subset.rs. Specifically, when matching a generic callable target with inference vars, pyrefly now checks the return type before the parameter list (is_subset_eq(&l.ret, &u.ret) before is_subset_params). Additionally, the new is_subset_overload_candidate method changes how overload candidates are evaluated — it now tolerates instantiation errors (restoring snapshot instead of failing). This change in overload resolution order likely affects how pandas .sum() overloads are resolved, causing pyrefly to select a different (broader) overload for winning_trades['profit_abs'].sum() and similar operations, resulting in the return type being inferred as Series[float] | Series | float | Unknown instead of just float.

more-itertools (+1)

The error Iterable[Literal[0]] for the iter2 parameter is nonsensical — it indicates pyrefly picked the wrong overload for pow or incorrectly narrowed the type during inference. The PR's change to check return types before parameters when inference vars are present caused a regression in overload selection for this map(pow, ...) call. This is a false positive introduced by the PR.
Attribution: The change in pyrefly/lib/solver/subset.rs to is_subset_overload_candidate() and the reordering of return-type-before-params checking in the callable subset logic (lines 1643-1654) altered overload resolution. When want contains inference variables, pyrefly now checks the return type first. This appears to cause incorrect overload selection for pow (or map.__new__), locking in Literal[0] as the expected type for the second iterable parameter instead of the general int type.

setuptools (+1)

This is a false positive introduced by the PR's overload selection changes. The code map(pow, repeat(x), reversed(range(n))) is valid — pow accepts numeric arguments and reversed(range(n)) produces int values. Pyrefly incorrectly infers Iterable[Literal[0]] as the expected type for the third argument, which indicates it selected the wrong overload of pow. The error is pyrefly-only, confirming it's a regression in overload resolution.
Attribution: The change in pyrefly/lib/solver/subset.rs to is_subset_overload_candidate() and the reordering of return-type-before-params checking in the callable subset logic (lines 1643-1651 of the diff) caused pyrefly to select the wrong overload for pow when used inside map(). By checking return types first when inference vars are present, pyrefly likely locks onto a narrow overload of pow (one returning Literal[0] or similar) before considering whether the parameter types are compatible, leading to the spurious Iterable[Literal[0]] constraint on iter2.

pandas-stubs (+16)

assert-type failures on pandas Series aggregation methods: All 16 errors are assert_type(Unknown, float) failed on Series.mean(), .median(), .std(), .var() across bool, int, float, complex, timestamp, and timedelta Series types. The PR's overload resolution change (reordering return-type vs params checking in subset.rs) causes pyrefly to select the wrong overload or fail to resolve the return type, yielding Unknown instead of the correct float/pd.Timestamp/pd.Timedelta. Neither mypy nor pyright flag these. This is a regression — the overload fix for itertools.accumulate has a negative side effect on pandas overload resolution.

Overall: The analysis is factually accurate. All 16 errors are indeed assert_type(Unknown, float) failures (and similar for pd.Timestamp/pd.Timedelta types) on pandas Series aggregation methods (.mean(), .median(), .std(), .var()). The error message assert_type(Unknown, float) failed correctly indicates pyrefly is inferring Unknown instead of the expected float return type. The analysis correctly notes that 0/16 errors appear in mypy and 0/16 in pyright, confirming this is a pyrefly-specific regression. The attribution to the PR's overload resolution change (reordering return-type vs parameter checking when inference variables are present) is a reasonable explanation for why pyrefly now fails to resolve these overloaded method return types, yielding Unknown. The pandas stubs do correctly type these methods to return float for numeric Series types, and the test code confirms this with assert_type annotations. The analysis correctly identifies this as a regression in pyrefly's overload resolution.

Attribution: The PR changed overload resolution in pyrefly/lib/solver/subset.rs. Specifically, the change in is_subset_overload_candidate() and the reordering of return-type-before-params checking when want_has_vars is true (lines 1643-1651) altered how overloads are selected for generic callables. This changed which overload is selected for pd.Series.mean(), .median(), .std(), .var() — the pandas stubs define these as overloaded methods, and the new overload selection order now picks a different overload that returns a type other than float (likely Unknown or some other type), causing the assert_type(..., float) checks to fail. The PR was intended to fix itertools.accumulate overload selection but had a side effect on pandas Series aggregation method overload resolution.

pytest-robotframework (+1, -1)

Both the old and new errors correctly identify the type error at line 64, where a () -> None function is passed where a function returning AbstractContextManager is expected. The old error message showed the expected parameter type as () -> AbstractContextManager[@_] — the ParamSpec was successfully resolved to empty parameters () (matching the argument's signature), with only the return type's type variable @_ remaining unresolved. The new error message shows (ParamSpec(@_)) -> AbstractContextManager[@_] — both the ParamSpec and the return type variable are unresolved. This represents a regression in type inference: the ParamSpec that was previously being correctly inferred is no longer being resolved. While the error is still correctly reported (the function e with return type None doesn't satisfy the AbstractContextManager return type requirement), the inference is less complete. The unresolved ParamSpec(@_) in the new message indicates the type checker is doing less work to resolve generic parameters than before. This is a regression in inference quality — the error detection is preserved but the type inference is degraded.
Attribution: The change in pyrefly/lib/solver/subset.rs at the callable matching section (around line 1643-1651) reorders the checking to evaluate return type before parameter list when the target contains placeholder variables. This changed how the _WrappedContextManagerKeywordDecorator.__call__ overload is resolved. Previously, parameter matching resolved the ParamSpec first (yielding () -> AbstractContextManager[@_]), but now return type is checked first, and the ParamSpec remains unresolved as ParamSpec(@_). The is_subset_overload_candidate function also changed overload selection to be more lenient with instantiation errors, which may have affected which overload was selected.

xarray (+1)

This is a false positive (regression). The code is correct: get_public_class_attrs returns set[str], the generator yields set[str] values, and functools.reduce(set.union, generator_of_set_str) is valid. The error message shows Iterable[set[Unknown]] which indicates pyrefly failed to properly infer the type variable — Unknown where str should be. The PR's change to overload resolution ordering (checking return types before parameters when inference vars are present) appears to have caused functools.reduce's overload selection to go wrong for this case. Neither mypy nor pyright flag this code.
Attribution: The PR changed overload matching in pyrefly/lib/solver/subset.rs by (1) reordering return type vs parameter checking when the want type has inference vars (is_subset_eq on return first, then params), and (2) introducing is_subset_overload_candidate which is more lenient with instantiation errors. The change to check return types before parameters when want_has_vars (lines around 1643-1651 in subset.rs) likely affected how functools.reduce's overloads are resolved. functools.reduce has multiple overloads, and the new ordering may cause pyrefly to select a different (wrong) overload, leading to set[Unknown] instead of set[str] in the inferred type for the iterable parameter. The Unknown in the error message (Iterable[set[Unknown]]) is a telltale sign of an inference failure — pyrefly picked an overload where it couldn't fully resolve the type variables.

spark (+3, -3)

New bad-argument-type errors with @_ inference failure: The 3 new errors contain _SupportsAdd[@_] which is a leaked placeholder variable — a type inference failure. Series[float | None].sum() is valid pandas code. Neither mypy nor pyright flag these. The PR's overload resolution changes caused the inference variable to not be resolved, making the error messages worse than before (which had _SupportsAdd[float]).
Removed bad-argument-type errors: The 3 removed errors were false positives (same lines, same root cause — rejecting valid .sum() calls on pandas Series). However, they were replaced by equivalent false positives with worse type inference (@_ instead of float), so the removal is not a net improvement.

Overall: This is a net-neutral-to-slightly-worse change. Before the PR, pyrefly reported 3 false positive errors on Series.sum() calls with _SupportsAdd[float] in the message. After the PR, pyrefly still reports 3 false positive errors on the exact same lines, but now with _SupportsAdd[@_] — indicating the type inference actually got worse (the @_ is a leaked placeholder variable, per rule 6 about @_ inference failures). The errors are at the same locations (lines 31, 44, 57) and have the same root cause: pyrefly incorrectly rejects Series[float | None].sum(). Neither mypy nor pyright flag these. The presence of @_ in the new error messages is a clear sign of inference degradation.

Attribution: The change in pyrefly/lib/solver/subset.rs at the is_subset_overload_candidate function and the callable matching reorder (checking return type before params when want_has_vars) changed how overload resolution works. The new is_subset_overload_candidate method uses snapshots to avoid committing inference variable bindings during overload candidate checking. This changed which overload of Series.sum gets selected, resulting in the type parameter being resolved to @_ (an unresolved placeholder) instead of float. The @_ in the error message _SupportsAdd[@_] indicates the inference variable was not properly resolved during overload selection.

➖ Neutral (13)

openlibrary (+1, -1)

map() constructor callable inference: The old error (@_) -> @_ and new error (@_) -> ImportItem are both false positives on map(ImportItem, result). Both mypy (no error) and pyright (accepts it) confirm this is valid code. The PR improved return type resolution (now shows ImportItem instead of @_) but the parameter inference variable @_ still prevents correct matching of type[ImportItem] against the callable parameter. Net effect is neutral — same false positive with a slightly more informative error message.

Overall: The code map(ImportItem, result) is valid Python. ImportItem is a class inheriting from web.storage, and classes are callable — type[ImportItem] should be assignable to the func parameter of map(). The error is a false positive in both the old and new versions of pyrefly. Notably, mypy does NOT flag this line (the annotation shows [mypy: no]), and pyright also accepts it ([pyright: yes]), confirming this is a pyrefly-specific false positive.

The PR improved the return type inference — the new error shows (@_) -> ImportItem instead of (@_) -> @_, meaning the return type of the callable is now correctly resolved to ImportItem. However, the parameter side still shows @_ (an unresolved inference variable), which prevents pyrefly from correctly matching type[ImportItem] against the expected callable type. The root cause — incomplete inference of map's generic type parameters when a class constructor is passed — remains unfixed.

Since both the old and new versions produce a false positive on the same line, and the error message is slightly more informative (showing the resolved return type), this is roughly neutral. The false positive persists but with marginally better diagnostic information.

Attribution: The PR changes pyrefly/lib/solver/subset.rs to check the return type before the parameter list when the want type contains placeholder variables (want_has_vars). This is the change at line 1663-1670 in the diff. Additionally, the is_subset_overload_candidate method (lines 152-167) now handles instantiation errors more gracefully during overload resolution. The net effect is that pyrefly now resolves the return type of the callable (getting ImportItem instead of @_) but still fails to fully resolve the parameter type, changing the error message from (@_) -> @_ to (@_) -> ImportItem. The error persists because the overload selection for map.__new__ still can't match type[ImportItem] to the expected callable signature.

antidote (+1, -1)

Callable inference message change: The error message changed from (Any, ParamSpec(@_)) -> @_ to (Any, ParamSpec(@_)) -> object — the return type is now resolved. The error itself (bad-argument-type) persists at the same location. This is a minor improvement in error message quality (less @_ noise) but not a behavioral change.

Overall: This is essentially a neutral change — the same error is reported at the same location, but with a slightly different message. The return type in the expected signature changed from @_ (unresolved inference variable) to object (resolved). Both the old and new errors flag the same issue: a () -> object function being passed to inject.method which expects a method with a self parameter. The error is arguably correct in spirit (the function signature doesn't match), but the @_ / ParamSpec(@_) artifacts in both versions suggest incomplete inference. The fact that pyright also flags this line (evidenced by the # pyright: ignore comment on line 549) suggests there is a real type issue here, though the specific error message differs. The net change is cosmetic — the return type portion of the error message improved from an unresolved variable to a concrete type.

Attribution: The change in pyrefly/lib/solver/subset.rs at the is_subset_eq for callable types now checks the return type before the parameter list when the target contains placeholder variables (want_has_vars). This changed the order of type resolution, which caused the return type to be resolved to object instead of remaining as @_. The is_subset_overload_candidate function also changed how overload matching handles instantiation errors (restoring snapshots on instantiation errors rather than failing).

egglog-python (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

anyio (+5, -5)

This is a neutral change. Both before and after the PR, pyrefly reports 5 false positive bad-argument-type errors on the same 5 lines in src/anyio/_core/_fileio.py. The errors involve passing bound methods like self._path.exists, self._path.group, self._path.is_dir, etc. to to_thread.run_sync(). The code is correct and works at runtime. Neither mypy nor pyright flag any of these. The @_ types in the error messages ((**tuple[*@_]) -> bool) indicate inference variable failures — pyrefly cannot properly resolve the generic callable parameter of run_sync when given these bound methods. The PR changed the error messages slightly: the return type TypeVar is now resolved (e.g., bool instead of @_), while the parameter tuple TypeVar remains unresolved. The expected type changed from (**tuple[*@_]) -> @_ to (**tuple[*@_]) -> bool. This represents a minor improvement in type inference (the return type is now correctly resolved) but the underlying false positive remains because the parameter type is still not properly resolved. Since 5 false positives were removed and 5 equivalent false positives were added at the same locations with slightly different messages, this is effectively neutral.
Attribution: The change in pyrefly/lib/solver/subset.rs in the is_subset_eq match arm for Callable types (lines ~1643-1654) now checks the return type before parameters when the target callable has inference variables (want_has_vars). Additionally, the new is_subset_overload_candidate() method (lines ~152-168) changes how overload candidates are matched by using snapshots that tolerate instantiation errors. These changes cause pyrefly to select a different overload of run_sync — one that now constrains the return type to bool but still fails on the parameter matching (the @_ in (**tuple[*@_]) -> bool). The net effect is that the error messages changed slightly (return type is now resolved to bool instead of @_) but the errors persist because the parameter inference still fails.

Expression (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

pwndbg (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

schemathesis (+2, -2)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

bandersnatch (+1, -1)

This is a neutral change — both the old and new errors are false positives (the code is valid and works at runtime). The error message changed from (**tuple[*@_]) -> @_ to (**tuple[*@_]) -> bool, showing that the PR's change to check return types first successfully resolved the return type inference, but the overall error persists because the parameter inference still fails (the @_ in tuple[*@_] indicates an unresolved inference variable). Neither mypy nor pyright flags this. The net effect is: one false positive was removed, one slightly-different false positive was added. The error quality improved marginally (return type is now concrete bool instead of @_), but it's still a false positive.
Attribution: The change in pyrefly/lib/solver/subset.rs at the callable matching logic (lines 1643-1651) now checks is_subset_eq(&l.ret, &u.ret) before is_subset_params(&l.params, &u.params) when the want type contains placeholder vars. This successfully resolved the return type from @_ to bool (visible in the error message change), but the parameter matching still produces the @_-containing type (**tuple[*@_]), so the error persists with a slightly different message. The is_subset_overload_candidate changes in the same file affect overload selection but the core issue here is the callable subset check order change.

mypy (+9, -9)

bad-argument-type on @classmethod: 7 new errors flagging valid @classmethod decorators in builtins.pyi (type.prepare, int.from_bytes, float.fromhex, etc.). These are false positives — the error messages contain @_ types indicating inference failures when matching the decorated function against classmethod.__init__'s parameter. Neither mypy nor pyright flags these. The 7 removed errors were the same false positives but with @_ appearing in both parameter types and return type (e.g., -> @_), whereas the new errors have resolved the return type but still fail on parameter type inference (e.g., -> MutableMapping[str, object] instead of -> @_). Net effect: same number of false positives, slightly different manifestation with partial improvement in return type resolution.
invalid-overload on dict.fromkeys: 2 new errors claiming fromkeys is not callable after @classmethod decorator application. The new errors show classmethod[Unknown, Ellipsis, dict[_T, Any | None]] (partially resolved return type) vs the old classmethod[Unknown, Ellipsis, Unknown] (fully unresolved). Both are false positives — dict.fromkeys is a valid classmethod that works correctly. The PR's inference reordering resolved the return type but still fails overall, producing a slightly different but equally wrong error.

Overall: This is a net-neutral change in terms of error count (9 added, 9 removed), but the errors are all false positives on well-known typeshed stubs (builtins.pyi). The @classmethod decorator on type.__prepare__, int.from_bytes, float.fromhex, bytes.fromhex, bytearray.fromhex, object.__subclasshook__, and dict.fromkeys are all valid and accepted by mypy and pyright. The new errors contain @_ types indicating inference failures in the parameter types. The PR changed the inference ordering for callable matching, which shifted the error manifestation but didn't fix the underlying issue with classmethod type parameter inference. Both old and new errors are false positives — the character of the errors changed (the old errors had @_ in both parameter types and return type, while the new errors resolved the return type but still have @_ in parameter types), but the net effect is the same: pyrefly incorrectly flags valid @classmethod usage in typeshed.

Attribution: The change in pyrefly/lib/solver/subset.rs in the is_subset_eq match arm for Callable types (around line 1660) now checks return type before params when the target has inference variables (want_has_vars). Additionally, the new is_subset_overload_candidate method treats InstantiationErrors as Ok (non-fatal), changing overload matching behavior. These changes altered how pyrefly resolves type variables when matching @classmethod-decorated functions against classmethod.__init__'s parameter type Callable[Concatenate[type[_T], _P], _R_co]. The new ordering resolves the return type first (now correctly getting MutableMapping[str, object] or dict[_T, Any | None] instead of Unknown), but then fails on the parameter matching, producing a different but still incorrect error message.

werkzeug (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

core (+3, -3)

Changed error messages for async_add_executor_job with Path methods: The 3 removed errors had target type (**tuple[*@_]) -> @_ and the 3 new errors have target type (**tuple[*@_]) -> bool. The PR's change to check return types before parameters when inference vars are present successfully resolved the return type but didn't fix the parameter inference. These remain false positives — the code is correct in terms of the argument being passed (bound methods like Path.exists and Path.is_file are valid callables for async_add_executor_job), and neither mypy nor pyright flags the argument type. Net effect is neutral: same number of false positives, slightly different wording.

Overall: This is a net-neutral change. The PR fixed the return type inference (the target type now shows -> bool instead of -> @_), but the parameter inference still fails (showing (**tuple[*@_]) -> bool). The errors were false positives before and remain false positives after — just with slightly different error messages. The @_ in the parameter types indicates ongoing inference failures. Neither mypy nor pyright flags these lines because passing Path.exists / Path.is_file to async_add_executor_job is perfectly valid code. The 3 removed errors and 3 added errors are essentially the same false positive with a cosmetic change in the error message (return type resolved from @_ to bool). Note: in local_source.py:305, the source code is actually missing an await (self.hass.async_add_executor_job(media_path.is_file) without await), which is a real bug in the source code, but the type error being reported is about the argument type mismatch for the target parameter, not the missing await.

Attribution: The PR changed pyrefly/lib/solver/subset.rs in two ways: (1) Added is_subset_overload_candidate() which uses snapshot-based checking for overload candidates, and (2) Changed callable subset checking to check return type before params when the target contains placeholder vars (want_has_vars). The return-type-first change in the Callable matching branch (lines ~1643-1654) caused the target type to resolve differently — previously the return type was @_ (unresolved), now it resolves to bool. However, the parameter matching still fails, producing (**tuple[*@_]) -> bool instead of (**tuple[*@_]) -> @_. So the error message changed (return type now resolved) but the fundamental inference failure on the parameter side persists.

attrs (+2, -2)

This is a net-neutral change. The same two errors exist before and after at the same locations. One error message improved (line 104: @_float in return type), while the other slightly degraded (line 25: ConvertibleToInt@_ in first param). The total error count is unchanged (2 before, 2 after). Both are legitimate type mismatches that pyright also flags.
Attribution: The change to is_subset_eqis_subset_overload_candidate in pyrefly/lib/solver/subset.rs at line 1159 and 1211, plus the return-type-first check at line 1663-1669, changed which overload candidate gets selected during error reporting, altering the error messages.

prefect (+1, -1)

This is a neutral-to-slight-improvement change. The same real bug is flagged at the same location — do_run_migrations is typed as taking AsyncEngine but run_sync passes a Connection. The only difference is the error message quality: the expected type changed from (Connection, ParamSpec(@_)) -> @_ to (Connection, ParamSpec(@_)) -> None, meaning pyrefly now correctly resolves the return type to None instead of leaving it as an unresolved inference variable. The @_ in the ParamSpec position is not an inference failure — it's the expected representation when no additional arguments are passed. The structural signal about @_ suggesting inference failure is misleading here; the @_ is in the ParamSpec (which is inherently unresolved when no extra args exist), and the return type actually improved from @_ to None.
Attribution: The change in pyrefly/lib/solver/subset.rs at the is_subset_eq for callable types (around line 1640) now checks the return type before parameters when the target contains placeholder variables (want_has_vars). This allows the return type to be resolved first, which then helps constrain the ParamSpec. Previously, checking params first left the return type unresolved (@_). The new is_subset_overload_candidate method also contributes by handling snapshot restoration for overload candidates more gracefully.

Suggested fixes

Summary: The PR's changes to overload resolution ordering (checking return types before parameters when inference vars are present) and the new is_subset_overload_candidate snapshot-based matching cause 22+ pyrefly-only regressions across 7 projects, primarily due to incorrect overload selection and inference failures yielding Unknown/@_ types.

1. In is_subset_overload_candidate() in pyrefly/lib/solver/subset.rs, the current implementation treats InstantiationErrors as Ok (restoring the snapshot but returning success). This is too lenient — it allows overload candidates that don't actually match to be selected, causing downstream inference failures (Unknown types for pandas .mean()/.sum()/.std()/.var(), wrong overloads for pow/reduce). Instead, InstantiationErrors should be treated as errors (like the original is_subset_eq behavior), or at minimum the snapshot should NOT be restored so that partial bindings from the successful parts of matching are preserved. Change the InstantiationErrors arm from self.solver.restore_vars(snapshot); Ok(()) to Err(SubsetError::new(...)) or keep the original is_subset_eq call for overload candidate matching.

Files: pyrefly/lib/solver/subset.rs
Confidence: high
Affected projects: pandas-stubs, xarray, freqtrade, spark, more-itertools, setuptools
Fixes: bad-argument-type, assert-type
The is_subset_overload_candidate function was introduced to replace is_subset_eq at lines 1156 and 1208 for overload matching. By treating InstantiationErrors as Ok, it allows overload candidates that partially fail to be accepted. This causes pyrefly to select wrong overloads for pandas Series aggregation methods (returning Unknown instead of float), functools.reduce (set[Unknown] instead of set[str]), and pow (Literal[0] instead of int). Reverting to the original is_subset_eq behavior for overload candidate matching would fix 16 pandas-stubs errors, 1 xarray error, 1 freqtrade error, and 3 spark errors (22 total across 5 projects). The test_bound_method_overload_assignable_to_callable test case may need a different fix approach.

2. In the Callable matching branch of is_subset_eq() in pyrefly/lib/solver/subset.rs (around line 1660), the conditional reordering that checks return type before parameters when want_has_vars is causing incorrect overload selection for built-in functions like pow() when used inside map(). The issue is that checking the return type first constrains inference variables prematurely, locking in a narrow overload (e.g., pow returning Literal[0]) before parameter types can guide selection. Consider removing the want_has_vars conditional and always checking params before return type (the original order), or only applying the reorder when the got type (not want) has vars.

Files: pyrefly/lib/solver/subset.rs
Confidence: medium
Affected projects: more-itertools, setuptools, pytest-robotframework
Fixes: bad-argument-type
The original code always checked params then return type. The new code checks return type first when want contains placeholder vars. For map(pow, ...), the want type is something like Callable[[T1, T2], T3] with inference vars. Checking the return type first constrains T3 prematurely, which then constrains which overload of pow is selected, leading to Literal[0] instead of int. This causes the more-itertools and setuptools regressions (2 errors). The freqtrade, pandas-stubs, and xarray regressions also stem from this reordering causing wrong overload selection. However, the test case test_bound_method_overload_assignable_to_callable suggests this reordering was needed for itertools.accumulate — so a more targeted fix might be needed, perhaps only applying the reorder when the got type is an overload (not a single callable).

3. A combined fix: keep the return-type-first ordering in the Callable branch of is_subset_eq() ONLY when both (a) want_has_vars is true AND (b) the got type is NOT from an overload resolution context. In is_subset_overload_candidate(), revert to using is_subset_eq directly (removing the InstantiationErrors-as-Ok logic), and instead fix the itertools.accumulate case by improving how bound method types are resolved before overload matching begins.

Files: pyrefly/lib/solver/subset.rs
Confidence: medium
Affected projects: freqtrade, more-itertools, setuptools, pandas-stubs, pytest-robotframework, xarray, spark
Fixes: bad-argument-type, assert-type
The PR was trying to fix itertools.accumulate overload resolution (the test case shows _FORMAT_PATH = '{}/{}'.format being passed to accumulate). The two changes (return-type-first ordering + lenient overload candidate matching) together cause 22+ regressions. A more surgical approach would be to fix the specific case (bound method overload assignability) without broadly changing overload resolution semantics. This would eliminate all 22+ regression errors across all 7 affected projects while preserving the fix for the accumulate test case.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (5 heuristic, 15 LLM)

@rchen152
Copy link
Copy Markdown
Contributor

FYI the linked issue no longer reproduces - I think it was fixed by some recent overload changes I made.

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.

Incorrect overload selection for itertools.accumulate

3 participants