Handler-bind with stateful closure handler is inconsistent
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
SBCL |
Fix Released
|
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:/
$ 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-
More information about SBCL is available at <http://
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))
(handler-bind ((condition handler))
(signal 'condition)
(signal 'condition)))
(assert (equalp '(2 1) list)))
(let (list)
(handler-bind ((condition (let ((x 0))
(signal 'condition)
(signal 'condition))
(assert (equalp '(1 1) list)))
NIL
* NIL
* *features*
(:X86-64 :GENCGC :64-BIT :ANSI-CL :COMMON-LISP :ELF :IEEE-FLOATING-
:LITTLE-ENDIAN :PACKAGE-
: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 |
Changed in sbcl: | |
status: | Fix Committed → Fix Released |
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.