Subject: Re: clisp: COMPILE-FILE needs two LOADs?
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 07 Feb 2006 22:47:11 -0600
Newsgroups: comp.lang.lisp
Message-ID: <qbWdncvS1u1S6nTeRVn-qw@speakeasy.net>
Edi Weitz  <spamtrap@agharta.de> wrote:
+---------------
|   (defconstant +foo+ 42)
|   (defun frob (arg)
|     (declare (ignore arg))
|     nil)
|   (define-compiler-macro frob (&whole form &environment env arg)
|     (cond ((constantp arg env) t)
|           (t form)))
|   (defun quux ()
|     (frob +foo+))
|   ----------------------> foo.lisp <----------------------
| 
| Start CLISP[1] and then try
| 
|   (load (compile-file "foo.lisp"))
|   (quux)
| 
| This returns NIL.  In LispWorks, CMUCL, and AllegroCL it returns T.
+---------------

Note that even though CMUCL passes your above test, it still has an
issue users need to watch out for involving the *value* of a DEFCONSTANT
not being available at compile time, even though your (QUUX) worked
fine. [Tim Bradshaw and I ran into this with Tim's HTOUT macro, which
was failing to compile on CMUCL when fed a constant variable name.]
If you add the following lines to your "foo.lisp":

    (defmacro frob2 (arg &environment env)
      (if (and (constantp arg env)
	       (equal 42 (symbol-value arg)))
	''win
	''lose))

    (defun quux2 ()
      (frob2 +foo+))

and then try to compile it, you get an UNBOUND-SYMBOL-ERROR:

    > (compile-file "foo.lisp")
    ...
    ; Converted FROB2.
    ; Compiling DEFMACRO FROB2: 

    ; File: /usr/u/rpw3/foo.lisp
    ; In: DEFUN QUUX2
    ;   (FROB2 +FOO+)
    ; Error: (during macroexpansion)
    ; Error in KERNEL::UNBOUND-SYMBOL-ERROR-HANDLER:  the variable +FOO+ is unbound.
    ...
    ; Compilation unit finished.
    ;   1 error
    ; foo.x86f written.
    > (load *)

    ; Loading #p"/usr/u/rpw3/foo.x86f".
    T
    > (quux)

    T
    > (quux2)

    Execution of a form compiled with errors:
     (FROB2 +FOO+)
       [Condition of type KERNEL:SIMPLE-PROGRAM-ERROR]
    Restarts:
      0: [ABORT] Return to Top-Level.

Of course, now that it's been loaded once [and thus the DEFCONSTANT
has actually *executed* once], everything will "just work" now:

    > (load (compile-file "foo.lisp"))
    ...[happy chatter]...
    T
    > (quux)

    T
    > (quux2)

    WIN
    > 

This gave Tim & me [well, mostly me, since he wasn't using CMUCL]
a ton of grief for a while, and we were just about to file a bug
until we noticed that according to the CLHS CMUCL's behavior is
*PERFECTLY LEGAL* [however counterintuitive it might seem to be so!]:

    Macro DEFCONSTANT
    ...
    If a defconstant form appears as a top level form, the compiler must
    recognize that name names a constant variable. An implementation
    may choose to evaluate the value-form 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.

Notice that this explicitly permits an implementation to evaluate
the "value-form" *ONLY* at load time (as CMUCL does), and in such
implementations the value will *not* be available (e.g., to macros
or compiler-macros) at compile time!! [Absent an EVAL-WHEN around
the DEFCONSTANT, of course...] What *will* be available is the state
of *being* a constant [using CONSTANTP], but not the value itself.
Said another way... Replace my FROB2/QUUX2 with these:

    (defmacro frob3 (arg &environment env)
      (if (and (constantp arg env)
	       (boundp arg))
	''win
	''lose))

    (defun quux3 ()
      (frob3 +foo+))

Then what you get is this:

    > (load (compile-file "foo.lisp"))
    ...[happy chatter]...
    T
    > (quux)

    T
    > (quux3)

    LOSE
    > 


-Rob

p.s. The above is the case in CMUCL-18e through -19c.

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607