(import (rnrs)) (with-exception-handler (lambda (k) #t) (lambda () (guard (e (#f #f)) (error 'test "msg"))))I couldn't figure it out why this never returned at first glance. And it turned out to be very interesting problem.
The problem is related to continuation boundary and exception handlers. If you print the k, then only the very first time you'd see error object with
"msg"after that it'd be
"attempt to return from C continuation boundary.". But why?
This happens when you capture a continuation and invoke it in the different C level apply. Each time C level apply is called, then VM put a boundary mark on its stack to synchronise the C level stack and VM stack. This is needed because there's no way to restore C level stack when the function is returned. When a continuation is captured on the C apply which is already returned, then invocation of this continuation would cause unexpected result. To avoid this, the VM checks if the continuation is captured on the same C level apply.
Still, why this combination causes this? The answer is
raiseis implemented in C world and exception handlers are invoked by C level apply. The whole step of the infinite loop is like this:
guardwithout else clause captures a continuation.
erroris called, then
guardre-raise and invoke the captured continuation. (required by R6RS/R7RS)
- Exception handler is invoked by C level apply.
- VM check the continuation boundary and raises an error on the same dynamic environment as #3
- Goes to #3. Hurray!
- Create a specific condition and when
with-exception-handlerreceived it, then it wouldn't restore the exception handler. [AdHoc]
raiseuse the Scheme level apply. (Big task)