I ran into a bizzare problem today which took some hours to debug.
I'm using SBCL 184.108.40.206.
Here's the scenario:
0. I'm not using optimization. In fact, I have debug 3 and speed 0.
1. I'm using read-macros to convert @symbols into a restart-case around
it that uses the :use-value restart.
2. In code which used the @symbol, I handle the unbound-variable signal.
3. I perform a side effect of marking something in a table related to the
name of the @symbol.
4. And invoke the restart of use-value with nil.
The expression in which you might see something like the above is:
(defun stuff ()
And it is wrapped in a handler-bind like this:
(invoke-restart :use-value nil))))
What happens is this:
It apparently seems that under some conditions, but not all,
if SBCL notices that the value of the variable can't possibly be
used, it doesn't even try to evaluate it. This is, in my opinion,
erroneous, since it will also fail to signal the unbound-variable
condition. When the code executes, debug statements inserted into the
read macro just before the evaluation of the unbound-variable fire,
but then nothing, and execution continues. It is as if the unbound
variable vanished all together. I've looked at my expanded macros
with sb-cltl2:macroexpand-all and the forms are correct. The compiler
just doesn't evaluate what is written in them.
Has anyone seen this before? Is this SBCL specific or a well-known
thing? While I've figured out basically why it happens, but I can't
find a way to work around it in the appropriate code layers. How can
I force the evaluation of something regardless of what the compiler
I'm having trouble making a smaller example which shows the bug because
each time I try, it works. But in the larger codes, it fails.
I managed to kludge a workaround by introducing a let form into my
read-macro like this:
;; my read macro
(eval-when (:compile-toplevel :load-toplevel :execute)
#'(lambda (stream char)
(declare (ignore char))
(let ((obj (read stream t nil t)))
(assert (symbolp obj))
(declare (ignore c))
(locally #+sbcl(declare (sb-ext:muffle-conditions warning))
(let ((g ,obj))
(format nil "About to eval undefined label ~A~%"
(:use-value (value) value))))))
If instead of the let form, I just use ,obj as the return value for the
locally form, the restart-case (with the symbol I'm wrapping) ends up
in all the right later macro expansions. If I emit a "about to evaluate
unbound-variable" right before the final ,obj form it gets printed out,
but then nothing happens. It is as if the unbound-variable simply isn't
present. It seems a compiler dataflow issue since just doing the rebinding
in the let (or other minor tricks, like passing the ,obj through identity
or (car (cons ,obj 1)) doesn't fix it). I have to actually *use* the
variable somehow--in this case, the format.
Any ideas? I can provide the source in question which trips the bug
and smaller examples which don't.