Hi,
using the CMU Common Lisp Snapshot 2005-11 (19C) on Linux x86, I get
undesirable results for defstructs with b-o-a constructors like the
following:
(defstruct (foobar
(:constructor make-foobar
(xxx
&key (aaa nil) (bbb nil)
&aux
(foobar-data xxx)
(aaa (or aaa
(getf foobar-data :aaa)
1))
(bbb (or bbb
(getf foobar-data :bbb)
(1+ aaa))))))
(aaa (required-argument) :type fixnum)
(bbb (required-argument) :type fixnum))
The intent is to have a b-o-a constructor with keyword arguments with
more complicated defaults; in reality, the binding for FOOBAR-DATA is
a potentially expensive computation depending on XXX.
I believe the above is valid CL (cf. CLHS 3.4.6), and Clisp in fact
accepts it and produces the desired results:
[8]> (make-foobar nil)
#<FOOBAR :AAA 1 :BBB 2>
[9]> (make-foobar '(:a 5 :aaa 6))
#<FOOBAR :AAA 6 :BBB 7>
Compiling this definition in CMUCL, however, gives me warnings and
notes about elimination of code,
; In: DEFSTRUCT FOOBAR
; (GETF FOOBAR-DATA :AAA)
; Note: Deleting unreachable code.
;
; (GETF FOOBAR-DATA :BBB)
; Note: Deleting unreachable code.
;
; (1+ AAA)
; --> +
; ==>
; AAA
; Note: Deleting unreachable code.
;
; (DEFSTRUCT (FOOBAR
; #)
; (AAA # :TYPE FIXNUM)
; (BBB # :TYPE FIXNUM))
; ==>
; (C::%FUNCALL
; #<LAMBDA #x587DC2BD NAME= NIL TYPE= # WHERE-FROM= :DEFINED VARS= #>
; XXX
; NIL
; NIL)
; Warning: A possible binding of #:AAA-DEFAULTING-TEMP is not a
(VALUES &OPTIONAL FIXNUM &REST T):
; NIL
;
; Warning: A possible binding of #:BBB-DEFAULTING-TEMP is not a
(VALUES &OPTIONAL FIXNUM &REST T):
; NIL
;
; Compilation unit finished.
; 2 warnings
; 3 notes
and calls to make-foobar involving the defaulting behavior like those
given above raise TYPE-ERRORs: NIL is not of type FIXNUM.
Macroexpanding the DEFSTRUCT shows that the constructor is defined as
follows:
(DEFUN MAKE-FOOBAR
(XXX
&KEY (AAA NIL) (BBB NIL)
&AUX (FOOBAR-DATA XXX) (AAA (OR AAA (GETF FOOBAR-DATA :AAA) 1))
(BBB (OR BBB (GETF FOOBAR-DATA :BBB) (1+ AAA))))
(DECLARE (TYPE T XXX)
(TYPE FIXNUM AAA)
(TYPE FIXNUM BBB)
(TYPE T FOOBAR-DATA)
(TYPE FIXNUM AAA)
(TYPE FIXNUM BBB))
(LET ((#:G1760 (TRULY-THE FOOBAR (KERNEL:%MAKE-INSTANCE 3))))
(SETF (KERNEL:%INSTANCE-LAYOUT #:G1760)
(KERNEL::%GET-COMPILER-LAYOUT FOOBAR))
(SETF (KERNEL:%INSTANCE-REF #:G1760 1) AAA)
(SETF (KERNEL:%INSTANCE-REF #:G1760 2) BBB)
#:G1760))
I believe the declarations to be wrong. In my understanding the
correct expansion would be something like
(DEFUN MAKE-FOOBAR
(XXX
&KEY (AAA NIL) (BBB NIL)
&AUX (FOOBAR-DATA XXX)
(#:AAA (OR AAA (GETF FOOBAR-DATA :AAA) 1))
(#:BBB (OR BBB (GETF FOOBAR-DATA :BBB)
(LET ((AAA #:AAA)) (1+ AAA)))))
(DECLARE (TYPE T XXX)
(TYPE T AAA)
(TYPE T BBB)
(TYPE T FOOBAR-DATA)
(TYPE FIXNUM #:AAA)
(TYPE FIXNUM #:BBB))
(LET ((AAA #:AAA) (BBB #:BBB))
(LET ((#:G1760 (TRULY-THE FOOBAR (KERNEL:%MAKE-INSTANCE 3))))
(SETF (KERNEL:%INSTANCE-LAYOUT #:G1760)
(KERNEL::%GET-COMPILER-LAYOUT FOOBAR))
(SETF (KERNEL:%INSTANCE-REF #:G1760 1) AAA)
(SETF (KERNEL:%INSTANCE-REF #:G1760 2) BBB)
#:G1760)))
, where #:AAA and #:BBB are some gensyms. Another, more natural
option would be to simply cut the &aux arguments from the argument
list and to create the obvious let bindings, i.e.:
(DEFUN MAKE-FOOBAR
(XXX &KEY (AAA NIL) (BBB NIL))
(DECLARE (TYPE T XXX)
(TYPE T AAA)
(TYPE T BBB))
(LET* ((FOOBAR-DATA XXX)
(AAA (OR AAA (GETF FOOBAR-DATA :AAA) 1))
(BBB (OR BBB (GETF FOOBAR-DATA :BBB) (1+ AAA))))
(DECLARE (TYPE T FOOBAR-DATA)
(TYPE FIXNUM #:AAA)
(TYPE FIXNUM #:BBB))
(LET ((#:G1760 (TRULY-THE FOOBAR (KERNEL:%MAKE-INSTANCE 3))))
(SETF (KERNEL:%INSTANCE-LAYOUT #:G1760)
(KERNEL::%GET-COMPILER-LAYOUT FOOBAR))
(SETF (KERNEL:%INSTANCE-REF #:G1760 1) AAA)
(SETF (KERNEL:%INSTANCE-REF #:G1760 2) BBB)
#:G1760)))
Comments?
Thanks in advance,
Albert.