This seems to be because things get stuck in an infinite loop.
when exiting the dynamic scope, var gets varpopped
varpop invokes set-var
set-var invokes throw something
throw() causes var to get varpopped
which repeats the loop until something breaks. Right now what breaks is that each time throw() is called, it takes another exception handler off of the handler stack until none are left, and then the next throw() calls assert(handler != NULL) which fails.
There's a similar infinite loop if you just directly set a variable inside its own settor function
But in this case at least it's more obvious that you're doing something wrong. I think in this case you loop until you hit the point where the shell throws the max-eval-depth exception, and then you hit the same throw() infinite loop in the first example.
An infinite loop or exception during the varpush causes more reasonable behavior:
; # totally normal behavior
; es -c 'set-var = {throw something}; local (var = !) {}'
uncaught exception: something
; # throws an exception, but at least it's a reasonable one
; es -c 'set-var = {var = ()}; local (var = !) {}'
max-eval-depth exceeded
The immediately obvious fix to try for the assertion failures is to move the tophandler = handler->up line in throw() down below the varpop()s. This half-works, in that it stops the assertion failures from happening, but it doesn't stop the infinite loop from blowing the stack. Worse is that when the infinite loop causes max-eval-depth to get thrown, handling that exception just gets caught up in everything (because it's calling throw(), which is calling varpop(), etc...), and then the "you blew your stack" segfault happens despite the shell's best attempts at protecting itself!
My preferred alternative is for varpop to some exception-handling itself -- catch any exceptions from the settor function, finish popping the var, and then re-throw. That seems to produce the preferred behavior in each case here.
Minimal example:
This seems to be because things get stuck in an infinite loop.
var
getsvarpop
pedvarpop
invokesset-var
set-var
invokesthrow something
throw()
causesvar
to getvarpop
pedwhich repeats the loop until something breaks. Right now what breaks is that each time
throw()
is called, it takes another exceptionhandler
off of the handler stack until none are left, and then the nextthrow()
callsassert(handler != NULL)
which fails.There's a similar infinite loop if you just directly set a variable inside its own settor function
But in this case at least it's more obvious that you're doing something wrong. I think in this case you loop until you hit the point where the shell throws the
max-eval-depth
exception, and then you hit the samethrow()
infinite loop in the first example.An infinite loop or exception during the
varpush
causes more reasonable behavior:The immediately obvious fix to try for the assertion failures is to move the
tophandler = handler->up
line inthrow()
down below thevarpop()
s. This half-works, in that it stops the assertion failures from happening, but it doesn't stop the infinite loop from blowing the stack. Worse is that when the infinite loop causesmax-eval-depth
to get thrown, handling that exception just gets caught up in everything (because it's callingthrow()
, which is callingvarpop()
, etc...), and then the "you blew your stack" segfault happens despite the shell's best attempts at protecting itself!My preferred alternative is for
varpop
to some exception-handling itself -- catch any exceptions from the settor function, finish popping the var, and then re-throw. That seems to produce the preferred behavior in each case here.