Earlier quoted context omitted.
Another approach is to do the opposite - always store all local variables in the heap, and rely on scalar replacement of aggregates to put them back onto the stack where possible. This is what I do in my Ruby interpreter. That’s definitely not compatible with a single-pass though!
A third option that can still be single pass is the Smalltalk way, where the stack frames themselves are first-class objects: the closures then keep a reference to the stack frame and the GC won't collect any live stack frames, which will contain closed-over variables.
Re: Crafting Interpreters: Closures
#61Yes that's really what I'm talking about, but plus the (really required for practical performance) optimisation of virtualising the frames onto the stack if they don't escape an activation.