abcl
abcl copied to clipboard
ABCL 1.8.0 cannot define a symbol macro with value NaN
When a Lisp file containing a symbol macro definition with value NaN is compiled, the abcl file's __loader__._
member does not contain a readable representation of the NaN. Loading the abcl file fails with error "Illegal # macro character #<". See the transcript below.
buggy __loader__._
in foo.abcl ... '#<SINGLE-FLOAT NaN>
is not readable
"; -*- Mode: Lisp -*-"
(SYSTEM:INIT-FASL :VERSION 43)
(SETQ SYSTEM:*SOURCE* #P"/tmp/foo.lisp")
(SYSTEM:PUT '#1=+FOO+ '#2=SYSTEM::SOURCE (CONS '(:SYMBOL-MACRO "/tmp/foo.lisp" 0)
(GET '#1# '#2# #3=NIL)))
(SYSTEM:PUT '#1=+FOO+ '#2=SYSTEM::SOURCE (CONS '(:SYMBOL-MACRO "/tmp/foo.lisp" 0)
(GET '#1# '#2# #3=NIL)))
(SYSTEM::%DEFINE-SYMBOL-MACRO '+FOO+ '#<SINGLE-FLOAT NaN>)
transcript ... compiling foo.lisp and trying to load the compiled abcl file fails
chuwi [95591] cat foo.lisp
(define-symbol-macro +foo+ #.(/ 0s0 0s0))
chuwi [95592] abcl --noinit
Armed Bear Common Lisp 1.8.0
Java 13.0.4 Private Build
OpenJDK 64-Bit Server VM
Low-level initialization completed in 0.689 seconds.
Startup completed in 3.488 seconds.
Type ":help" for a list of available commands.
CL-USER(1): (compile-file "foo.lisp")
; Compiling /tmp/foo.lisp ...
; (DEFINE-SYMBOL-MACRO +FOO+ ...)
; Wrote /tmp/foo.abcl (0.134 seconds)
#P"/tmp/foo.abcl"
NIL
NIL
CL-USER(2): (load "foo.abcl")
Error loading /tmp/foo.abcl at line 10 (offset 357)
#<THREAD "interpreter" {7F7850DE}>: Debugger invoked on condition of type READER-ERROR
Illegal # macro character: #\<
Restarts:
0: TOP-LEVEL Return to top level.
[1] CL-USER(3):
Hmm. The ABCL representations of float NaNs are deliberately non-readable objects as they get processed by the condition handlers present in a running program when they are encountered.
I note that SBCL and CCL emit errors compiling your DEFINE-SYMBOL-MACRO
form, refusing to create a fasl.
If I had to "fix" things at this point, I would mimic the behavior of the other implementations.
@brown If I fixed it so that the ABCL compiler wouldn't emit such fasls, would that satisfy your bug report? Is there perhaps a deeper thing you are trying to do here that wouldn't be fixed by such a patch?
@brown if you are looking to represent such a value aeth (via #lisp) points to https://github.com/Shinmera/float-features/blob/master/float-features.lisp.
Otherwise I am inclined at this point to patch the ABCL compiler to emit an error here.
Neither CCL 1.11 nor SBCL 2.0.8 produce an error when compiling and loading the following code:
(define-symbol-macro +double-float-nan+
#+abcl #.(system:make-double-float (ash -1 32))
#+ccl ccl::double-float-nan
#+sbcl #.(sb-kernel:make-double-float -1 0)
#-(or abcl ccl sbcl) (error "unimplemented"))
ABCL emits an error when loading the abcl file produced when the file is compiled. It should definitely not do that. I believe it should not generate an error at compile time or load time.
The right fix might be to print NaNs readably when creating the loader entry of the abcl file, but I haven't explored exactly what CCL and SBCL do in their FASL files. Note that ABCL can print NaNs readably:
CL-USER(1): (let ((*print-readably* t)) (print (/ 0s0 0s0)))
#.(CL:PROGN "Comment: create a NaN." (CL:/ 0.0s0 0.0s0))
#<SINGLE-FLOAT NaN>
The float features Lisp source file you referenced does not contain any code to directly create NaNs, but I can probably use the floating point mode setting macro to make one. I am generating Lisp code to represent protocol buffers, which allow a protobuf field to be initialized to a float infinity or NaN. With the symbol macro definition above my generator can output code that initializes an instance slot to +double-float-nan+.
I'm not sure why I made symbol macros for the NaN constants and I'm not sure why I used a read-time value either. If I don't use a read-time value, then ABCL is happy with the expression that makes a double-float NaN ... so I have something that works. Maybe the whole issue is what a Common Lisp system should do with symbol macro definitions when the value is not readably printable. I'll have to try defining a symbol macro in SBCL that's set to a read-time evaluated hash table, or something similar. Maybe it does not rely on print/read the way ABCL does in its FASL file format.
I'm glad you found a work-around that doesn't require a new ABCL for you.
I'm still trying to figure out the best way to patch the implementation to bring it in line with what one gets with SBCL/CCL, and will try to work this out in the next day or so.
Thanks for the feedback, and I will let you know here when I have a working proposal.
@brown if you are looking to represent such a value aeth (via #lisp) points to https://github.com/Shinmera/float-features/blob/master/float-features.lisp.
As corrected on #lisp, it was noted that FLOAT-FEATURES doesn't currently support cross-implementation serialization of NaN float values: perhaps it should?
@guicho271828 has filed https://github.com/Shinmera/float-features/issues/10 to track (eventual) implementation candidates
I also got the same error when trying to compile and load a file with something like this:
(defparameter *some-hash-table* #.(make-hash-table :test 'equalp))
The compiled .abcl file result was like:
`"; -- Mode: Lisp --"
(SYSTEM:INIT-FASL :VERSION 43)
(COMMON-LISP:SETQ SYSTEM:SOURCE #P"/tmp/test.lisp")
(COMMON-LISP:IN-PACKAGE "COMMON-LISP-USER") (PROGN (SYSTEM:PUT '#1=SOME-HASH-TABLE '#2=SYSTEM::SOURCE (CONS (LIST :VARIABLE "/tmp/test.lisp" 0) (GET '#1# '#2# #3=NIL))) (DEFPARAMETER #1# #<EQUALP HASH-TABLE 0 entries, 11 buckets {C9AA145}>))`
Maybe an idea to solve this is to follow the CLISP approach, they print hashtables in a readable way, using structuctures: The CLISP output for this snippet is:
(|SYSTEM|::|VERSION| '(20100807.)) #0Y_ #0Y |CHARSET|::|UTF-8| #Y(#:|1 2 (DEFPARAMETER *SOME-HASH-TABLE* #S(HASH-TABLE :TEST EQUALP))-1| #20Y(00 00 00 00 00 00 00 00 20 01 DA 31 62 DB DC 31 5A C6 19 01) ((|COMMON-LISP|::|SPECIAL| |COMMON-LISP-USER|::|*SOME-HASH-TABLE*|) |COMMON-LISP-USER|::|*SOME-HASH-TABLE*| #S(|COMMON-LISP|::|HASH-TABLE| :|TEST| |COMMON-LISP|::|EQUALP|)) (|COMMON-LISP|::|T| |COMMON-LISP|::|T| |COMMON-LISP|::|T|))