Skip to content

Preserve surviving bindings for repeated unpack targets - #17

Open
EiNSTeiN- wants to merge 2 commits into
ejfkdev:mainfrom
EiNSTeiN-:fix/repeated-unpack-bindings
Open

EiNSTeiN- wants to merge 2 commits into
ejfkdev:mainfrom
EiNSTeiN-:fix/repeated-unpack-bindings

Conversation

@EiNSTeiN-

Copy link
Copy Markdown

I am opening this PR to preserve surviving bindings when Python optimizes away repeated unpack targets. The code below currently overwrites the real _ binding during decompilation:

def select(values):
    return [(value, discarded, _)
            for value, _, discarded, discarded in values]

It is currently reconstructed as:

def select(values):
    return [(value, discarded, _)
            for value, _, _, discarded in values]

For [(1, 2, 3, 4)], the source returns [(1, 4, 2)]; the incorrect reconstruction returns [(1, 4, 3)] because the third slot overwrites _.

Earlier stores to a repeated target can become POP_TOP. Replacing every discarded slot with an invented _ changes local bindings, local-name metadata and the recompilation's store/elision pattern. The same distinction applies to ordinary loop targets such as for value, discarded, discarded, discarded in values.

The parser now looks only within the unfinished unpack's direct stores and reuses the surviving local/cell target's spelling. It stops at nested unpacking, control flow or effects, and retains _ as a fallback when no surviving target is proved. Genuine underscore bindings remain distinct from eliminated stores to another name.

The optimization appears in CPython 3.13's STORE_FAST optimizer, which replaces an adjacent overwritten store on the same source line with POP_TOP. The inference therefore starts at 3.13, including inlined list comprehensions and ordinary unpacking loops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant