Specifically:
1. We were tracking the stack allocation both in handleScopeInst and in the
Stack. We should only track it in one of them. I also used this as an
opportunity to make sure the code worked for non_nested code.
2. I made it so that we properly handle the end tracking part of the code so we
handle the token/stack part of begin_apply correctly. I generalized the code so
that we should handle non_nested stack allocations as well.
I included tests that validated that we now handle this correctly.
This also required me to change how we handled which instruction/argument we
emit an error about in the verifier. Previously we were using two global
variables that we made nullptr to control which thing we emitted an error about.
This was unnecessary. Instead I added a little helper struct that internally
controls what we will emit an error about and an external "guard" RAII struct
that makes sure we push/pop the instruction/argument we are erroring upon
correctly.
edges into dead-end regions.
- Only treat edges *into* dead-end regions as special; edges internal
the region must use the normal rules.
- Conservatively merge information along those edges rather than just
picking one at random. This requires us to not walk into the region
until we've processed all of the edges to it.
- Make sure we prevent *any* stack allocations from being deallocated
if the stack is inconsistent entering a block.
Additionally, fix a bug which was incorrectly treating all blocks that
don't themselves exit the function as ultimately leading to unreachable,
which had inadvertently largely turned off the consistency check.