Handler-bind with stateful closure handler is inconsistent

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

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 2.1.1.116-ef9328e12, 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)))

NIL
* NIL
* *features*
(:X86-64 :GENCGC :64-BIT :ANSI-CL :COMMON-LISP :ELF :IEEE-FLOATING-POINT :LINUX
 :LITTLE-ENDIAN :PACKAGE-LOCAL-NICKNAMES :SB-CORE-COMPRESSION :SB-LDB
 :SB-PACKAGE-LOCKS :SB-THREAD :SB-UNICODE :SBCL :UNIX)

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)))

RUNNING
MAKING-A-HANDLER
RUNNING
MAKING-A-HANDLER
NIL

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