[Openmcl-devel] setf gethash on constant hashtable does not work

Gary Byers gb at clozure.com
Mon Aug 29 02:28:03 PDT 2005


This is maybe a little subtle.  I'm not sure that there's
a bug here per se, but it's not clear that this is entirely
desirable behavior either.

The spec leaves it up to the implementation whether the value in a
DEFCONSTANT form is evaluated at compile time, load time, or both.
"Therefore, users must ensure that the initial-value can be evaluated
at compile time (regardless of whether or not references to name
appear in the file) and that it always evaluates to the same value."

It's hard to ensure that a the call to MAKE-HASH-TABLE that might
occur during compile-time evaluation of the initial-value returns
"the same value" as the call to MAKE-HASH-TABLE that'll occur at
load time.  (In fact it's not possible to do that.)

The compiler's allowed to treat a reference to a constant symbol
as if it were a literal reference to that constant's value.  (It's
not supposed to matter whether that's the constant's compile-time
value or its load-time value, since those are supposed to be the
same.)  OpenMCL substitutes the compile-time value and does this
pretty freely; I believe that some other implementations are more
cautious about doing this kind of substitution when issues of
identity/mutability might arise.

So, the effect of executing:

(setf (gethash 1 constant-table) :one)
(setf (gethash 2 constant-table) :two)

at load time is to do (SETF GETHASH) on a literal hash table
object that's equivalent to the hash table that's equivalent
to the one that existed at compile time (both calls will refer
to the same literal object), and the load-time effect of the
earlier

(defconstant constant-table (make-hash-table))

will have been to create a (different) hash table, set CONSTANT-TABLE's
value to that hash table, and ensure that CONSTANT-TABLE is treated
as a constant.

As I said, I think that that's all perfectly legal (and the policies
that govern that behavior are generally useful), but it's not
necessarily how most people would expect your example to behave.

Since implementations are allowed a lot of latitude, it's not clear
to me if there's a 100% portable way of doing what you intend.  One
non-portable way that'd work in OpenMCL (and possibly some other
implementations, possibly not others) is:

(eval-when (:compile-toplevel)
   ;; Notice that the concept of compile-time constants -
   ;; while certainly useful - isn't generally portable.
   (defconstant compile-time-hash-table (make-hash-table)))

(defconstant constant-table compile-time-hash-table)
(defparameter variable-table (make-hash-table))

(setf (gethash 1 constant-table) :one)
(setf (gethash 2 constant-table) :two)
(setf (gethash 1 variable-table) :one)
(setf (gethash 2 variable-table) :two))

(The effect there is to make the initial-value form for CONSTANT-TABLE
be a literal HASH-TABLE object at both compile-time and load-time.  Of
course it's not "the same object" at load-time as it was at compile-time,
but all of the references to it within that file will be consistent.)

The other approach (which references a mechanism that really should be
documented) involves advising the compiler to be less eager to substitute
constant symbols' values.  The incantation is:

? (ccl:set-current-file-compiler-policy
      (ccl:new-compiler-policy
        :allow-constant-substitution
           #'(lambda (var val env)
               (declare (ignore var env))
               (not (typep val 'hash-table)))))

If that were the default policy, the first suggested workaround
wouldn't work.

On Mon, 29 Aug 2005, David Tolpin wrote:

> Hi,
>
> test.lisp
> ==========
>
> (defconstant constant-table (make-hash-table))
> (defparameter variable-table (make-hash-table))
>
> (setf (gethash 1 constant-table) :one)
> (setf (gethash 2 constant-table) :two)
> (setf (gethash 1 variable-table) :one)
> (setf (gethash 2 variable-table) :two)
>
> ==========
>
>
> ? (compile-file "test")
> #P"/Users/dvd/Workplace/RenderX/XEPNG/src/modules/font-server/test.dfsl"
> NIL
> NIL
> ? (load "test")
> #P"/Users/dvd/Workplace/RenderX/XEPNG/src/modules/font-server/test.dfsl"
> ? constant-table
> #<HASH-TABLE :TEST EQL size 0/60 #x84BBB46>
> ? variable-table
> #<HASH-TABLE :TEST EQL size 2/60 #x84BB296>
> ?
>
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel
>
>



More information about the Openmcl-devel mailing list