sasagawa888/eisl

Condition handler changes inside a with-handler code body

Closed this issue · 9 comments

When executing this code:

(defun handler (condition)
  (continue-condition condition "continued"))

(with-handler #'handler
              (print (cerror "foo" "bar"))
              (print (cerror "herp" "derp")))

The first cerror call is continued as expected, and prints "continued".
The second cerror call triggers the debugger when it attempts to continue.

looking at the code in error.c

eisl/error.c

Lines 645 to 651 in 1f4c3ff

if (error_handler != NIL) {
int handler;
handler = car(error_handler);
error_handler = cdr(error_handler);
return (apply(handler, list1(x)));
}

It appears that the latest handler is popped off globally every time signal-condition is called.

The specification states that

This newly established handler is active throughout execution of its associated body of code unless shadowed by another use of with-handler.

So the handler should stay the same between the two cerror calls, and "continued" should be printed twice.

Thanks for the report. I ponder

Fixed. Is the fixe working?

Unfortunately not, I'm afraid.
According to the specification:

A handler may defer to previously established handlers by calling signal-condition on the condition object which it received as an argument.

So the below code should print:

"inner handler continued"
"inner handler continued"
"outer handler continued"
(defglobal error-count 0)

(defun outer-handler (condition)
  (continue-condition condition "outer handler continued"))

(defun inner-handler (condition)
  (if (< error-count 2) 
      (progn 
        (setf error-count (+ error-count 1))
        (continue-condition condition "inner handler continued"))
      (signal-condition condition (condition-continuable condition))))

(defun foo ()
  (with-handler #'outer-handler 
                (with-handler #'inner-handler
                              (print (cerror "foo" "bar"))
                              (print (cerror "herp" "derp"))
                              (print (cerror "bing" "bong")))))

Instead, this code crashes eisl because it calls the inner handler forever when the last error is signaled.

Thank you. I will try again.

I fixed. is it working?

I found another bug:
The code below should print:

"handled"
"handled"

Instead it prints "handled" one time and triggers the debugger. This seems to be related to how error handlers are popped off when signal-condition is called.

(defun handler (condition)
  (throw 'tag "handled"))

(defun baz ()
  (with-handler #'handler
                (print (catch 'tag
                              (error "error")))
                (print (catch 'tag
                              (error "error")))))

Fixed.

Easy-ISLisp Ver2.93
> (load "tests/bug.lsp")
T
> (baz)
"handled"
"handled"
NIL
> 

Thank you for your hard work, Mr. Sasagawa. Everything seems to work now.

Thank you for your help in fixing the bug.