Repository navigation
Conversation
Fixes facebook#3412. For `isinstance(x, (A, B))`, pyrefly previously computed the negative branch by subtracting each tuple element from the input type independently and then intersecting the results. When the input contained a type that overlapped with multiple tuple elements (e.g. `Iterable[Any]` is iterable and is therefore a supertype of both `str` and `bytes`), the intersection step would reintroduce the subtracted alternatives, so a: float | str | bytes | Iterable[Any] if isinstance(a, (str, bytes)): ... else: reveal_type(a) # was Iterable[Any] | bytes | float | str came out unchanged. The fix walks the input union once and subtracts every tuple element from each member in turn, so members that ARE in the tuple drop out and members that aren't (`float`, `Iterable[Any]`) are kept. Adds a regression test in `pyrefly/lib/test/narrow.rs`.
b5371c8 to
2a86c1b
Compare
Primer Diff Classification✅ 5 improvement(s) | ➖ 4 neutral | 9 project(s) total | +6, -15 errors 5 improvement(s) across pandera, strawberry, static-frame, kornia, spark.
Detailed analysis✅ Improvement (5)pandera (-1)
strawberry (-1)
static-frame (-1)
kornia (-4)
spark (-2)
➖ Neutral (4)bokeh (+1, -1)
ibis (+2, -2)
werkzeug (+1, -1)
scrapy (+2, -2)
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (4 heuristic, 5 LLM) |
|
@yangdanny97 has imported this pull request. If you are a Meta employee, you can view this in D105741367. |
stroxler
left a comment
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.

Fixes #3412.
This fixes negative narrowing for
isinstancechecks where the target is a tuple of runtime classes, such asisinstance(x, (str, bytes)).Summary
isinstance(x, (A, B)),narrow_is_not_instancepreviously subtracted each tuple element from the full input type independently and then intersected the per-element results.Iterable[Any]is a supertype of bothstrandbytes), the intersection step would reintroduce the subtracted alternatives through subset-based intersection. Concretely,intersect_impl(Iterable[Any], str)returnsstrbecausestr <: Iterable[Any].type[C]variable) are skipped, preserving existing conservative behavior.Repro
Before:
Iterable[Any] | bytes | float | strAfter:
Iterable[Any] | floatTest plan
test_isinstance_tuple_negative_with_overlapinpyrefly/lib/test/narrow.rs.cargo test -p pyrefly --lib— 5152 passed, 0 failed.test_isinstance_*tests continue to pass, including the existingtest_isinstance_type_negative_partial_narrowwhich exercises the not-finaltype[C]skip path.target/debug/pyrefly.