I think what you might be missing is that you can also do a sort of livenes analysis to determine how many dynamically-sized variables are "awake" at the same time at most. Then you only need to create as many stacks as that number, because you can reuse the stacks of "sleeping" variables (since the "awake" variables are guaranteed to be freed once their block goes out of scope, and threads don't share any memory so no data race worries regarding those stacks).

For example, suppose we have a function with a growable array that calls another function (without passing said array) that also uses a growable array as some temporary scratch space. Then the first function's array isn't being used when the second is sleeping, so the inner function's array could safely share the stack because it will all be freed with a single pointer restore once the second function exits.

Now, I actually don't know how the compiler works but as I understand it does things similar to my example to optimize the number of stacks needed (but let's wait for WvO to confirm or deny whether my understanding matches reality).

The obvious question is how does it work for linked lists, trees, and other non-stack-shaped data structures?

At the end of the day trees are still stored linearly in memory. Think of it as an array, with each node an element of the array, and indexes used rather than pointers.

Plenty of trees only ever grow. And that's the normal type of array in Goose. However you can also have arrays that can grow and shrink, if you have a tree that is constantly in flux.

Either way it'll be one stack for an entire tree.