This change causes closures for nested functions to capture their
enclosing functions' variables by reference. Even though an inner
function cannot update outer variables, it must observe updates to
them made by the outer function.
A special case of this is a nested recursive function f:
def outer():
def f(): ...f()...
The def f statement constructs a closure which captures f, and then
binds the closure value to f. If the closure captures by value (as
before this change), the def statement will fail because f is
undefined (see issue #170). Now, the closure captures a reference
to f, so it is safe to execute before f has been assigned.
This is implemented as follows. During resolving, captured local
variables such as f are marked as as "cells". The compiler assumes and
guarantees that such locals are values of a special internal type
called 'cell', and it emits explicit instructions to load from and
store into the cell. At runtime, cells are created on entry to the function;
parameters may be "spilled" into cells as needed.
Each cell variable gets its own allocation to avoid spurious liveness.
A function's tuple of free variables contains only cells.
This change causes closures for nested functions to capture their enclosing functions' variables by reference. Even though an inner function cannot update outer variables, it must observe updates to them made by the outer function.
A special case of this is a nested recursive function f:
def outer(): def f(): ...f()...
The def f statement constructs a closure which captures f, and then binds the closure value to f. If the closure captures by value (as before this change), the def statement will fail because f is undefined (see issue #170). Now, the closure captures a reference to f, so it is safe to execute before f has been assigned.
This is implemented as follows. During resolving, captured local variables such as f are marked as as "cells". The compiler assumes and guarantees that such locals are values of a special internal type called 'cell', and it emits explicit instructions to load from and store into the cell. At runtime, cells are created on entry to the function; parameters may be "spilled" into cells as needed. Each cell variable gets its own allocation to avoid spurious liveness. A function's tuple of free variables contains only cells.
Fixes #170