From: Peter K. <ps...@cs...> - 2010-11-29 08:39:20
|
Hello, I ran into a bizzare problem today which took some hours to debug. I'm using SBCL 1.0.43.82. 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 () @things (do-something) @stuff (do-something-else)) And it is wrapped in a handler-bind like this: (handler-bind ((unbound-variable #'(lambda (c) (do-side-effect c) (invoke-restart :use-value nil)))) (stuff)) 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 thinks? 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) (set-macro-character #\@ #'(lambda (stream char) (declare (ignore char)) (let ((obj (read stream t nil t))) (assert (symbolp obj)) `(restart-case (handler-bind ((warning #'(lambda (c) (declare (ignore c)) (muffle-warning)))) (locally #+sbcl(declare (sb-ext:muffle-conditions warning)) (let ((g ,obj)) (format nil "About to eval undefined label ~A~%" g) g))) (: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. Thank you. -pete |
From: Nikodemus S. <nik...@ra...> - 2010-11-29 10:26:54
|
On 29 November 2010 10:16, Peter Keller <ps...@cs...> wrote: > 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. SBCL *should* preserve references to non-local variables whose value is unused when SAFETY is 3. The language in the CLHS is "should be signaled" which means the error is required only for safe code -- and SBCL uses this freedom to eliminate the variable reference if its value is unused. (Note that default SAFETY is 1.) If you make your read-macro produce (locally (declare (optimize safety)) my-unbound-var) does the elimination still happen? If it does, even a large example that demonstrate the issue is most welcome, since that sounds like an SBCL bug. Cheers, -- Nikodemus |
From: Peter K. <ps...@cs...> - 2010-11-29 17:19:26
|
On Mon, Nov 29, 2010 at 12:26:47PM +0200, Nikodemus Siivola wrote: > On 29 November 2010 10:16, Peter Keller <ps...@cs...> wrote: > > > 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. > > SBCL *should* preserve references to non-local variables whose value > is unused when SAFETY is 3. The language in the CLHS is "should be > signaled" which means the error is required only for safe code -- and > SBCL uses this freedom to eliminate the variable reference if its > value is unused. > > (Note that default SAFETY is 1.) > > If you make your read-macro produce > > (locally (declare (optimize safety)) > my-unbound-var) > > does the elimination still happen? I tried your suggesstion, but it still did not work as intended. > If it does, even a large example that demonstrate the issue is most > welcome, since that sounds like an SBCL bug. I've produced a tarball which exhibits this behavior. After you untar it, read the README, which will hopefully explain what you need to know and how to reproduce the problem and how it looks when it works with my workaround. http://pages.cs.wisc.edu/~psilord/pic.tar.gz One minor addendum is that if you add (safety 3) in the declaim at the top of pic/pic-instruction-sets/12-bit/12-bit.lisp, you still get the problem. If you have any questions, please let me know. Thank you. -pete |