Conversation
A function defined inside a function can read and write the names around it, and `nonlocal` says which of them it means to write. A name shared that way lives in a cell both frames hold rather than a register in each, which is the difference between a closure and a copy. Which names need a cell is a fact about the slot rather than about any one read of it, and is not known until the body is finished, because the `def` that captures a name can come after every use of it. So the HIR records it on the slot and the bytecode compiler is the one pass that consults it. Nothing that is not a closure pays anything: the call benchmark is unchanged. A `nonlocal` with nothing to bind to is now a `SyntaxError` rather than a report of a missing feature, which meant giving the lowering a second kind of failure. All four of CPython's messages are word for word, as is the `NameError` for a free variable with nothing in it.
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.
A function defined inside a function can now read and write the names around it, and
nonlocalsays which of them it means to write.A name shared that way lives in a cell both frames hold rather than in a register in each of them, which is the whole difference between a closure and a copy. Two functions defined in the same frame see each other's writes, a counter carries on counting between calls, and every
defin a loop closes over the one loop variable rather than its value at the time, so all three print 3. A name captured two functions deep is carried by the function in between whether that function mentions it or not, because a capture list only reaches the frame that wrote thedef.Which names need a cell is a fact about the slot rather than about any one read of it, and it is not known until the body is finished, because the
defthat captures a name can come after every use of it. So the HIR records it on the slot, the printer spells it out on every use (cell x,free x), and the bytecode compiler is the one pass that consults it. That keeps the lowering to a single pass and means nothing that is not a closure pays for any of this: a function capturing nothing compiles to exactly what it did before, and the call benchmark is unchanged at 0.11s.A
nonlocalwith nothing to bind to is aSyntaxErrorrather than a report that the feature is missing, which meant giving the lowering a second kind of failure. Calling it unsupported would send somebody looking through the milestones for it. All four of CPython's messages are word for word, along with theNameErrorfor a free variable with nothing in it, which is a different sentence from the one an ordinary unbound local gets. Every message was checked against a running 3.14.Part of #7.