Bug in Clisp?



Jaime Villate <villate at fe.up.pt> wrote:

   (defun foo (x &aux (y '(:foo 1)))
      (format t "bar: ~a" (getf y :bar)) (setf (getf y :bar) x))
   
   y is supposed to be a list that is given an initial value (:foo 1)
   everytime the function foo is called. That's how it works in SBCL and
   Clozure Lisp. However, in Clisp the value of y is persistent, as a
   global variable:

What you are missing is that in ANSI Common Lisp, any literal object in
code is immutable.  For example, it would be permissable for the (:foo
1) list in the above function to be stored in read-only memory, causing
some sort of exception or random corruption when setf of getf tries to
modify it.

Anything inside a quote special form, and various other places in the
language that are unevaluated constants, may not be modified by
"conforming code."  This restriction is defined int he ANS here:
http://www.franz.com/support/documentation/current/ansicl/dictentr/quote.htm

Not all implementations will implement constants as actually immutable,
but it is a source of subtle bugs when a constant is modified and not
detected and reported by the implementation.

The following is probably not what was intended:

 cl-user(31): (defun foo ()
		(let ((x '(0 1 2 3 4 5)))
		  (list (pop (cdr x)) (pop (cdr x)))))
 foo
 cl-user(32): (foo)
 (1 2)
 cl-user(33): (foo)
 (3 4)
 cl-user(34): (foo)
 (5 nil)
 cl-user(35): (foo)
 (nil nil)

So the conforming way to write a function like your ecxample
   
   (defun foo (x &aux (y '(:foo 1)))
     (format t "bar: ~a" (getf y :bar)) (setf (getf y :bar) x))

is

   (defun foo (x &aux (y (list :foo 1)))
     (format t "bar: ~a" (getf y :bar)) (setf (getf y :bar) x))

which ensures that the plist is consed freshly upon each call to the
function.  On the other hand, if you want this list to be persistent and
shared between all invocations, create it as a defvar or defparameter.