sasagawa888 / eisl

ISLisp interpreter/compiler
Other
272 stars 22 forks source link

Condition handler changes inside a with-handler code body #266

Closed gtnoble closed 1 year ago

gtnoble commented 1 year ago

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 https://github.com/sasagawa888/eisl/blob/1f4c3ff65cd7e436e53c5a668a963d425a6bff72/error.c#L645-L651 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.

sasagawa888 commented 1 year ago

Thanks for the report. I ponder

sasagawa888 commented 1 year ago

Fixed. Is the fixe working?

gtnoble commented 1 year ago

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.

sasagawa888 commented 1 year ago

Thank you. I will try again.

sasagawa888 commented 1 year ago

I fixed. is it working?

gtnoble commented 1 year ago

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")))))
sasagawa888 commented 1 year ago

Fixed.

Easy-ISLisp Ver2.93
> (load "tests/bug.lsp")
T
> (baz)
"handled"
"handled"
NIL
> 
gtnoble commented 1 year ago

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

sasagawa888 commented 1 year ago

Thank you for your help in fixing the bug.