Handler-bind with stateful closure handler is inconsistent

Bug #1916302 reported by Christophe on 2021-02-19
This bug affects 1 person
Affects Status Importance Assigned to Milestone

Bug Description

1) When using a stateful handler in HANDLER-BIND, the behavior changes whether or not the handler is inlined (edit: not in the INLINE declaration
sense, just written directly) or bound to a variable first (example below).

2) The preferred behavior as far as I am concerned would be to do as other implementations and allow the normal use of let-over-lambdas like in higher-order functions; this looks more aligned with the standard to me.

For context, see https://stackoverflow.com/questions/66282753/is-using-handler-bind-with-a-stateful-closure-valid

$ uname -a
Linux dehli 5.4.0-65-generic #73-Ubuntu SMP Mon Jan 18 17:25:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

$ sbcl --no-sysinit --no-userinit
This is SBCL, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (let (list)
  (let ((handler (let ((x 0))
                   (lambda (c)
                     (declare (ignore c))
                     (push (incf x) list)))))
    (handler-bind ((condition handler))
      (signal 'condition)
      (signal 'condition)))
  (assert (equalp '(2 1) list)))

(let (list)
  (handler-bind ((condition (let ((x 0))
                              (lambda (c)
                                (declare (ignore c))
                                (push (incf x) list)))))
    (signal 'condition)
    (signal 'condition))
  (assert (equalp '(1 1) list)))

* *features*

Here above, the first test behaves as I expect it to, the handler mutates its local state and push a different value in LIST each time it is executed.

In the second test, it is as-if the let-over-lambda was evaluated anew each time.

Thank you.

description: updated
Richard M Kreuter (kreuter) wrote :

Interesting catch. HANDLER-BIND turns out to wrap complex handler forms in a lambda, which delays evaluation of that form until the time of signaling, and also evaluates that form each time SIGNAL is called (which is why you're getting a fresh closure each time).

* (handler-bind ((condition (progn (print 'making-a-handler) (constantly nil))))
    (dotimes (i 2) (print 'running) (signal 'condition)))


Doesn't seem right to me. Attaching a patch that pulls out the evaluation of complex handler forms to happen just once.

Thanks, applied as 1074ef41921e195868dd552cedae931a75dffb56

Changed in sbcl:
status: New → Fix Committed
Christophe (junke-christophe) wrote :

Thank you

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers