Populate the enclosing Anywhere phi for walrus-in-comprehension targets#3699
Open
mikeleppane wants to merge 1 commit into
Open
Populate the enclosing Anywhere phi for walrus-in-comprehension targets#3699mikeleppane wants to merge 1 commit into
mikeleppane wants to merge 1 commit into
Conversation
A name assigned by >=2 walrus operators inside comprehensions becomes an `Anywhere` (SSA phi) binding in the enclosing scope. PEP 572 routes the write through `define_in_enclosing_non_comprehension_scope`, which updated only the enclosing flow and never recorded the phi branch that `bind_name` records for ordinary assignments. A read that resolves to the enclosing `Anywhere` static before the walrus write (e.g. a read-before-write, or a fuzzed for-target) then left the phi promised but value-less, panicking with "key lacking binding" at solve time. Have the redirect return the enclosing scope's `NameWriteInfo` and record the `Anywhere` bind, so the walrus fully emulates an enclosing-scope assignment. Fixes facebook#3670
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3670
What
Pyrefly panicked (
Internal error: key lacking binding, key=Key::Anywhere(c)) instead of type-checking. Reachable from valid Python, not just the fuzzed input in the issue:Why
A name assigned by ≥2 walrus operators inside comprehensions becomes an
Anywherebinding — pyrefly's SSA φ (phi) node whose value is the join of all assignment sites. The φ's value is only ever populated byrecord_bind_in_anywhere, reached throughbind_name. But PEP 572 says a comprehension walrus assigns to the enclosing scope, so it routes throughdefine_in_enclosing_non_comprehension_scope, which updated only the enclosing flow and never recorded the φ branch. When a read resolves to that enclosingAnywherestatic before the walrus write (a read-before-write, or the issue's malformed for-target that reads the name), the φ node is created with no value → panic at solve time. In ordinary code the redirect's flow write shadows the φ, masking the gap. mypy, pyright, and ty all handle this without crashing (the conformance bar): error on the early read, inferint/Literal[2]after.How
Make the walrus redirect fully emulate an enclosing-scope assignment — the same φ bookkeeping
bind_namedoes:pyrefly/lib/binding/scope.rs—define_in_enclosing_non_comprehension_scopenow returnsOption<NameWriteInfo>(the enclosing scope's static write-info), mirroringdefine_in_current_flow.pyrefly/lib/binding/bindings.rs— newbind_walrus_target_in_enclosing_scopehelper that, when the enclosing name isAnywhere, callsrecord_bind_in_anywhere(must live here —table/record_bind_in_anywhereare module-private).pyrefly/lib/binding/expr.rs— theExpr::Namedwalrus arm calls the helper instead of the bare redirect.Deliberately untouched: the single-walrus / no-
Anywherecase stays a no-op (anywhere_rangeisNone);bind_name's type-alias/final/annotation checks already run on the comprehension-scope binding.Test plan
pyrefly/lib/test/scope.rs)test_walrus_in_comprehension_anywhere_read_before_writeLiteral[2]aftertest_walrus_in_comprehension_anywhere_in_functiontest_walrus_in_comprehension_anywhere_nestedtest_walrus_in_comprehension_anywhere_genexptest_walrus_in_comprehension_two_walrus_no_early_readtest_walrus_comprehension_fuzz_no_crashcargo test -p pyrefly --lib→ 5360 passed;test::scope::→ 92 passed (all pre-existing walrus tests unchanged).