fix Pyrefly doesn't reveal type as Never in unreachable code #3202#3205
fix Pyrefly doesn't reveal type as Never in unreachable code #3202#3205asukaminato0721 wants to merge 2 commits intofacebook:mainfrom
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Diff from mypy_primer, showing the effect of this PR on open source code: pip (https://github.com/pypa/pip)
+ ERROR src/pip/_vendor/tomli_w/_writer.py:111:24-35: Expected class object, got `Never` [invalid-argument]
+ ERROR src/pip/_vendor/tomli_w/_writer.py:112:36-39: Argument `object` is not assignable to parameter `obj` with type `list[Unknown] | tuple[Unknown, ...]` in function `format_inline_array` [bad-argument-type]
+ ERROR src/pip/_vendor/tomli_w/_writer.py:219:25-36: Expected class object, got `Never` [invalid-argument]
openlibrary (https://github.com/internetarchive/openlibrary)
- ERROR openlibrary/plugins/upstream/models.py:561:25-38: Expected `__bool__` to be a callable, got `Unknown | None` [not-callable]
- ERROR openlibrary/plugins/upstream/models.py:563:28-35: Returned type `list[Edition | Image]` is not assignable to declared return type `list[Image]` [bad-return]
cloud-init (https://github.com/canonical/cloud-init)
+ ERROR tests/unittests/sources/test_oracle.py:1266:48-68: `str` is not assignable to attribute `imds_url_used` with type `Never` [bad-assignment]
aiohttp (https://github.com/aio-libs/aiohttp)
- ERROR aiohttp/multipart.py:325:20-32: Returned type `bytearray` is not assignable to declared return type `bytes` [bad-return]
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ ERROR test/mitmproxy/addons/test_view.py:561:30-35: `Literal['GET']` is not assignable to attribute `method` with type `Never` [bad-assignment]
urllib3 (https://github.com/urllib3/urllib3)
- ERROR test/test_response.py:955:21-22: `Literal[2]` is not assignable to attribute `_fp` with type `HTTPResponse | None` [bad-assignment]
+ ERROR test/test_response.py:955:21-22: `Literal[2]` is not assignable to attribute `_fp` with type `Never` [bad-assignment]
pwndbg (https://github.com/pwndbg/pwndbg)
- ERROR pwndbg/aglib/dynamic.py:768:37-41: Cannot set item in `dict[str, type[Any]]` [unsupported-operation]
- ERROR pwndbg/commands/got_tracking.py:219:17-38: Expected a callable, got `None` [not-callable]
- ERROR pwndbg/commands/got_tracking.py:220:20-39: Object of class `NoneType` has no attribute `symtab_read` [missing-attribute]
- ERROR pwndbg/commands/got_tracking.py:221:47-61: Object of class `NoneType` has no attribute `string` [missing-attribute]
- ERROR pwndbg/commands/got_tracking.py:226:15-42: Object of class `NoneType` has no attribute `name` [missing-attribute]
- ERROR pwndbg/gdblib/got.py:412:20-49: Object of class `NoneType` has no attribute `has_field` [missing-attribute]
apprise (https://github.com/caronc/apprise)
- ERROR tests/test_config_http.py:315:26-41: `int` is not assignable to attribute `max_buffer_size` with type `Never` [bad-assignment]
- ERROR tests/test_config_http.py:348:26-41: `int` is not assignable to attribute `max_buffer_size` with type `Never` [bad-assignment]
- ERROR tests/test_plugin_exotel.py:428:28-42: Object of class `NoneType` has no attribute `url_id` [missing-attribute]
+ ERROR tests/test_plugin_matrix.py:761:24-37: `Literal['already.set']` is not assignable to attribute `home_server` with type `Never` [bad-assignment]
+ ERROR tests/test_plugin_matrix.py:766:24-37: `Literal['already.set']` is not assignable to attribute `home_server` with type `Never` [bad-assignment]
+ ERROR tests/test_plugin_pushbullet.py:361:28-368:22: `bytes` is not assignable to attribute `content` with type `Never` [bad-assignment]
+ ERROR tests/test_plugin_pushbullet.py:370:28-30: `dict[@_, @_]` is not assignable to attribute `headers` with type `Never` [bad-assignment]
+ ERROR tests/test_plugin_pushbullet.py:374:33-37: `Literal[b'}']` is not assignable to attribute `content` with type `Never` [bad-assignment]
+ ERROR tests/test_plugin_pushbullet.py:376:33-35: `dict[@_, @_]` is not assignable to attribute `headers` with type `Never` [bad-assignment]
- ERROR tests/test_utils_pem.py:121:26-33: Argument `str | None` is not assignable to parameter `encrypted_payload` with type `bytes | str` in function `apprise.utils.pem.ApprisePEMController.decrypt` [bad-argument-type]
- ERROR tests/test_utils_pem.py:220:15-30: Argument `bool | str | None` is not assignable to parameter `path` with type `PathLike[bytes] | PathLike[str] | bytes | str` in function `os.unlink` [bad-argument-type]
- ERROR tests/test_utils_pem.py:227:15-30: Argument `bool | str | None` is not assignable to parameter `path` with type `PathLike[bytes] | PathLike[str] | bytes | str` in function `os.unlink` [bad-argument-type]
mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
- ERROR pymongo/asynchronous/mongo_client.py:1249:17-24: Argument `dict[tuple[Never, Never], ServerDescription]` is not assignable to parameter `server_descriptions` with type `dict[tuple[str, int | None], ServerDescription]` in function `pymongo.topology_description.TopologyDescription.__init__` [bad-argument-type]
- ERROR pymongo/synchronous/mongo_client.py:1249:17-24: Argument `dict[tuple[Never, Never], ServerDescription]` is not assignable to parameter `server_descriptions` with type `dict[tuple[str, int | None], ServerDescription]` in function `pymongo.topology_description.TopologyDescription.__init__` [bad-argument-type]
pandera (https://github.com/pandera-dev/pandera)
+ ERROR pandera/api/dataframe/model.py:299:46-55: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR pandera/api/pyspark/model.py:204:46-55: Expected a type form, got instance of `Never` [not-a-type]
- ERROR tests/pandas/test_schema_components.py:589:36-58: Object of class `Iterable` has no attribute `tolist` [missing-attribute]
async-utils (https://github.com/mikeshardmind/async-utils)
- ERROR src/async_utils/_graphs.py:76:29-44: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:76:29-44: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:83:30-45: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:83:30-45: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:84:20-35: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:84:20-35: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:86:31-46: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:86:31-46: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:120:38-53: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:120:38-53: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:120:55-70: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:120:55-70: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:121:29-44: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:121:29-44: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:121:55-70: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:121:55-70: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:127:38-53: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:127:38-53: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:127:70-85: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:127:70-85: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:138:36-51: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:138:36-51: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:138:66-81: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:138:66-81: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:149:35-50: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:149:35-50: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:152:33-48: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:152:33-48: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:153:19-34: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:153:19-34: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:155:21-36: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:155:21-36: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:156:26-41: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:156:26-41: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:160:15-37: Expected a type form, got instance of `_SpecialForm` [not-a-type]
+ ERROR src/async_utils/_graphs.py:160:15-37: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:186:39-54: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:186:39-54: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/_graphs.py:201:37-52: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/_graphs.py:201:37-52: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/task_cache.py:86:40-41: Expected a type form, got instance of `ParamSpec` [not-a-type]
+ ERROR src/async_utils/task_cache.py:86:40-41: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/task_cache.py:86:43-44: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/task_cache.py:86:43-44: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/task_cache.py:86:59-60: Expected a type form, got instance of `ParamSpec` [not-a-type]
+ ERROR src/async_utils/task_cache.py:86:59-60: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/async_utils/task_cache.py:86:62-63: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/async_utils/task_cache.py:86:62-63: Expected a type form, got instance of `Never` [not-a-type]
build (https://github.com/pypa/build)
+ ERROR tests/test_env.py:323:16-35: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR tests/test_env.py:346:16-35: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/__main__.py:164:16-30: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/__main__.py:210:16-30: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/__main__.py:274:16-30: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/__main__.py:310:16-30: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/__main__.py:367:16-30: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/env.py:107:20-29: Expected a type form, got instance of `Never` [not-a-type]
+ ERROR src/build/env.py:109:25-34: Expected a type form, got instance of `Never` [not-a-type]
vision (https://github.com/pytorch/vision)
- ERROR torchvision/prototype/datasets/utils/_encoded.py:54:33-59: Argument `ReadOnlyTensorBuffer` is not assignable to parameter `fp` with type `IO[bytes] | PathLike[bytes] | PathLike[str] | bytes | str` in function `PIL.Image.open` [bad-argument-type]
- ERROR torchvision/transforms/_functional_pil.py:253:23-40: Argument `tuple[int, ...]` is not assignable to parameter `size` with type `list[int] | ndarray | tuple[int, int]` in function `PIL.Image.Image.resize` [bad-argument-type]
zulip (https://github.com/zulip/zulip)
+ ERROR corporate/tests/test_stripe.py:5762:39-50: `Literal['cus_12345']` is not assignable to attribute `stripe_customer_id` with type `Never` [bad-assignment]
+ ERROR zerver/tests/test_auth_backends.py:9335:31-64: `dict[str, str]` is not assignable to attribute `headers` with type `Never` [bad-assignment]
+ ERROR zerver/tests/test_auth_backends.py:9354:31-33: `dict[@_, @_]` is not assignable to attribute `headers` with type `Never` [bad-assignment]
- ERROR zerver/tests/test_upload_s3.py:714:55-64: `hex_value` may be uninitialized [unbound-name]
static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/core/archive_npy.py:239:37-42: `Literal[False]` is not assignable to attribute `writeable` with type `Never` [bad-assignment]
pandas (https://github.com/pandas-dev/pandas)
- ERROR pandas/core/frame.py:15674:24-35: Object of class `ndarray` has no attribute `_reduce` [missing-attribute]
+ ERROR pandas/core/generic.py:7180:49-53: Argument `int` is not assignable to parameter `iterable` with type `Iterable[@_]` in function `enumerate.__new__` [bad-argument-type]
+ ERROR pandas/core/resample.py:246:36-40: `Literal[True]` is not assignable to attribute `_is_resample` with type `Never` [bad-assignment]
- ERROR pandas/tests/arithmetic/test_period.py:1283:15-23: Object of class `NaTType` has no attribute `freq` [missing-attribute]
- ERROR pandas/tests/extension/date/array.py:115:23-32: Type `object_` is not iterable [not-iterable]
- ERROR pandas/tests/resample/test_datetime_index.py:406:15-30: Object of class `Index` has no attribute `as_unit` [missing-attribute]
pytest-robotframework (https://github.com/detachhead/pytest-robotframework)
- ERROR pytest_robotframework/__init__.py:136:35-37: `TracebackType | None` is not assignable to attribute `__traceback__` with type `Never` [bad-assignment]
meson (https://github.com/mesonbuild/meson)
+ ERROR mesonbuild/ast/introspection.py:372:41-44: Cannot set item in `dict[str, Never]` [unsupported-operation]
+ ERROR mesonbuild/ast/introspection.py:373:16-32: Returned type `dict[str, Never]` is not assignable to declared return type `dict[str, TYPE_var]` [bad-return]
- ERROR mesonbuild/modules/hotdoc.py:249:20-23: Returned type `list[CustomTarget | CustomTargetIndex | File]` is not assignable to declared return type `CustomTarget | CustomTargetIndex | File` [bad-return]
+ ERROR mesonbuild/optinterpreter.py:95:22-33: `str` is not assignable to attribute `file` with type `Never` [bad-assignment]
werkzeug (https://github.com/pallets/werkzeug)
- ERROR tests/test_wrappers.py:744:20-26: `timedelta` is not assignable to attribute `age` with type `Never` [bad-assignment]
- ERROR tests/test_wrappers.py:750:28-31: `datetime` is not assignable to attribute `retry_after` with type `Never` [bad-assignment]
pyodide (https://github.com/pyodide/pyodide)
- ERROR pyodide-build/pyodide_build/recipe/skeleton.py:418:47-54: Argument `list[Literal['sdist', 'wheel'] | str] | list[str]` is not assignable to parameter `source_types` with type `list[Literal['sdist', 'wheel']]` in function `_find_dist` [bad-argument-type]
+ ERROR pyodide-build/pyodide_build/recipe/skeleton.py:418:47-54: Argument `list[Literal['sdist', 'wheel'] | str]` is not assignable to parameter `source_types` with type `list[Literal['sdist', 'wheel']]` in function `_find_dist` [bad-argument-type]
core (https://github.com/home-assistant/core)
- ERROR homeassistant/components/ssdp/scanner.py:538:23-37: Cannot set item in `dict[Never, Never]` [unsupported-operation]
- ERROR homeassistant/components/ssdp/scanner.py:538:41-44: Cannot set item in `dict[Never, Never]` [unsupported-operation]
- ERROR homeassistant/components/ssdp/scanner.py:549:14-23: Argument `dict[Unknown, Unknown] | dict[Never, Never]` is not assignable to parameter `upnp` with type `Mapping[str, Any]` in function `homeassistant.helpers.service_info.ssdp.SsdpServiceInfo.__init__` [bad-argument-type]
sphinx (https://github.com/sphinx-doc/sphinx)
- ERROR sphinx/directives/patches.py:54:32-44: `int` is not assignable to attribute `line` with type `Never` [bad-assignment]
- ERROR sphinx/domains/cpp/_parser.py:1145:40-46: Argument `Literal['class', 'enum', 'struct', 'typename', 'union'] | None` is not assignable to parameter `prefix` with type `str` in function `sphinx.domains.cpp._ast.ASTTrailingTypeSpecName.__init__` [bad-argument-type]
+ ERROR sphinx/transforms/__init__.py:103:45-53: `BuildEnvironment` is not assignable to attribute `env` with type `Never` [bad-assignment]
xarray (https://github.com/pydata/xarray)
- ERROR xarray/core/indexing.py:180:28-32: Cannot set item in `dict[int, Index | PandasIndex]` [unsupported-operation]
- ERROR xarray/core/indexing.py:180:36-40: Cannot set item in `dict[int, Index | PandasIndex]` [unsupported-operation]
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], *tuple[type[Any], ...]], __config__=None, __validators__=dict[str, classmethod[Any, Ellipsis, Any] | classmethod[Any, Ellipsis, Unknown]], __cls_kwargs__=None, **dict[str, 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]], __cls_kwargs__=None, **dict[str, tuple[DeferredType, FieldInfo]]) [no-matching-overload]
- ERROR pydantic/v1/main.py:952:51-55: Default `None` is not assignable to parameter `__validators__` with type `dict[str, classmethod[Any, Ellipsis, Any] | classmethod[Any, Ellipsis, Unknown]]` [bad-function-definition]
+ ERROR pydantic/v1/main.py:952:51-55: Default `None` is not assignable to parameter `__validators__` with type `dict[str, classmethod[Any, Ellipsis, Any]]` [bad-function-definition]
- ERROR pydantic/v1/main.py:966:51-55: Default `None` is not assignable to parameter `__validators__` with type `dict[str, classmethod[Any, Ellipsis, Any] | classmethod[Any, Ellipsis, Unknown]]` [bad-function-definition]
+ ERROR pydantic/v1/main.py:966:51-55: Default `None` is not assignable to parameter `__validators__` with type `dict[str, classmethod[Any, Ellipsis, Any]]` [bad-function-definition]
- ERROR pydantic/v1/main.py:979:51-55: Default `None` is not assignable to parameter `__validators__` with type `dict[str, classmethod[Any, Ellipsis, Any] | classmethod[Any, Ellipsis, Unknown]]` [bad-function-definition]
+ ERROR pydantic/v1/main.py:979:51-55: Default `None` is not assignable to parameter `__validators__` with type `dict[str, classmethod[Any, Ellipsis, Any]]` [bad-function-definition]
schema_salad (https://github.com/common-workflow-language/schema_salad)
- ERROR schema_salad/sourceline.py:211:32-37: Argument `list[int] | list[Never]` is not assignable to parameter `lc` with type `list[int] | None` in function `cmap` [bad-argument-type]
+ ERROR schema_salad/sourceline.py:213:30-32: `str` is not assignable to attribute `filename` with type `Never` [bad-assignment]
+ ERROR schema_salad/sourceline.py:226:30-32: `str` is not assignable to attribute `filename` with type `Never` [bad-assignment]
prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/integrations/prefect-gcp/prefect_gcp/workers/cloud_run_v2.py:191:30-38: `str` is not assignable to attribute `_job_name` with type `Never` [bad-assignment]
|
Primer Diff Classification❌ 10 regression(s) | ✅ 13 improvement(s) | ➖ 3 neutral | 26 project(s) total | +64, -68 errors 10 regression(s) across openlibrary, cloud-init, aiohttp, mitmproxy, urllib3, pandera, async-utils, build, zulip, static-frame. error kinds:
Detailed analysis❌ Regression (10)openlibrary (-2)
cloud-init (+1)
aiohttp (-1)
mitmproxy (+1)
mod = tft(method="put", start=6) # line 556
v.add([mod]) # line 557
...
mod.request.method = "GET" # line 561 - ERRORLine 561 is clearly reachable code — there's no conditional branch making it unreachable. The The PR's logic in The Per-category reasoning:
urllib3 (+1, -1)
pandera (+2, -1)
async-utils (+24, -24)
build (+9)
zulip (+3, -1)
All 3 errors are pyrefly-only (neither mypy nor pyright flag them), and all involve The removed error ( Per-category reasoning:
static-frame (+1)
✅ Improvement (13)pip (+3)
pwndbg (-6)
apprise (+6, -6)
mongo-python-driver (-2)
vision (-2)
pandas (+2, -4)
Per-category reasoning:
pytest-robotframework (-1)
meson (+3, -1)
Net assessment: The 3 new errors involve
werkzeug (-2)
core (-3)
xarray (-2)
schema_salad (+2, -1)
prefect (-1)
➖ Neutral (3)pyodide (+1, -1)
sphinx (+1, -2)
pydantic (+4, -4)
Suggested fixesSummary: The new flow_narrow_exhaustive mechanism in current_flow_narrow_exhaustive_key() over-aggressively collects ALL active flow narrows and applies exhaustive Never-collapsing to any new binding in the flow, causing ~40+ false positive Never-inference errors across 10+ projects. 1. In
2. In
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (2 heuristic, 24 LLM) |
There was a problem hiding this comment.
Pull request overview
Fixes pyrefly’s handling of unreachable branches caused by flow narrowing so that names assigned inside an impossible branch can be treated as Never (aligning reveal_type / assert_never behavior with expectations from #3202).
Changes:
- Added a regression test covering an assignment in an unreachable (
Never) branch. - Threaded a new
flow_narrow_exhaustivekey throughNameAssignso the solver can collapse the assigned name’s type toNeverwhen the active narrows make the branch impossible. - Added scope helpers to collect active narrow bindings in the current flow and build an
Exhaustivebinding from them.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
pyrefly/lib/test/narrow.rs |
Adds a regression test asserting assigned reveals as Never in an unreachable branch. |
pyrefly/lib/binding/target.rs |
Computes and stores a flow_narrow_exhaustive key on NameAssign bindings. |
pyrefly/lib/binding/scope.rs |
Exposes current-flow narrow indices to support reachability/exhaustiveness checks. |
pyrefly/lib/binding/bindings.rs |
Builds an Exhaustive binding from current-flow narrows for later solve-time checks. |
pyrefly/lib/binding/binding.rs |
Extends NameAssign to carry the optional flow_narrow_exhaustive key. |
pyrefly/lib/alt/solve.rs |
Uses flow_narrow_exhaustive to collapse assigned-name type to Never (while still evaluating RHS for errors). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| flow_narrow_exhaustive: if is_new_binding_in_flow { | ||
| self.current_flow_narrow_exhaustive_key(name.range) | ||
| } else { | ||
| None | ||
| }, |
There was a problem hiding this comment.
flow_narrow_exhaustive is only set on the first value-binding in the current flow (is_new_binding_in_flow). In an impossible/narrowed-to-Never branch, a second assignment to the same name (still unreachable) will skip this and can “escape” back to a non-Never type. Consider attaching the exhaustiveness key for all NameAssigns that occur under active narrows (or otherwise keying off reachability), not just the first binding in the flow, so subsequent reassignments in unreachable code also collapse to Never.
| flow_narrow_exhaustive: if is_new_binding_in_flow { | |
| self.current_flow_narrow_exhaustive_key(name.range) | |
| } else { | |
| None | |
| }, | |
| flow_narrow_exhaustive: self.current_flow_narrow_exhaustive_key(name.range), |
| pub fn current_flow_narrow_idxs(&self) -> Vec<Idx<Key>> { | ||
| self.current() | ||
| .flow | ||
| .info | ||
| .iter() | ||
| .filter_map(|(_, info)| info.narrow.as_ref().map(|narrow| narrow.idx)) | ||
| .collect() |
There was a problem hiding this comment.
current_flow_narrow_idxs allocates a Vec and iterates over the entire flow.info map. Since this is used from assignment binding logic, it may be on hot paths in large functions. If performance becomes an issue, consider returning an iterator, or tracking active narrow idxs incrementally in Flow to avoid repeatedly scanning all names.
| pub fn current_flow_narrow_idxs(&self) -> Vec<Idx<Key>> { | |
| self.current() | |
| .flow | |
| .info | |
| .iter() | |
| .filter_map(|(_, info)| info.narrow.as_ref().map(|narrow| narrow.idx)) | |
| .collect() | |
| pub fn current_flow_narrow_idxs(&self) -> impl Iterator<Item = Idx<Key>> + '_ { | |
| self.current() | |
| .flow | |
| .info | |
| .values() | |
| .filter_map(|info| info.narrow.as_ref().map(|narrow| narrow.idx)) |
|
@kinto0 has imported this pull request. If you are a Meta employee, you can view this in D102371205. |
Summary
Fixes #3202
makes local name assignments inside a type-impossible branch resolve to Never instead of preserving the RHS literal type.
Test Plan
add test