Reputation: 69934
I know that if the safety setting is low, Common Lisp can use type annotations as optimization aids and are not checked. For example, this program runs and prints both the number and the string without any type errors. (I only get a type error in SBCL, when safety >= 1)
(declaim (optimize
(speed 3)
(safety 0)))
(defun f (x)
(declare (type fixnum x))
x)
(format t "1 ~A~%" (f 17))
(format t "2 ~A~%" (f "asd"))
I now wonder if its possible to create a program that does nasty things if safety is set to zero and type annotations are not respected. Things like casting from one type to another (like C type casts) and other kinds of undefined behavior.
So far, I haven't managed to find out an example that does that. I tried variations of this example that uses typed arrays but none of them resulted in typecasting behavior.
(declaim (optimize
(speed 3)
(safety 0)
))
(defun f ()
(let ((arr (make-array '(5)
:element-type 'fixnum
:initial-contents (list 1 2 3 4 5))))
(declare (type (vector fixnum 5) arr))
(setf (aref arr 0) "hello")
(aref arr 0)))
(format t "a1 ~A~%" (f))
In CLISP, this program prints "hello", without doing a typecast to int and in SBCL the program aborts with a SIMPLE-TYPE-ERROR error.
Is there a way to create a Common Lisp program that results in demons coming out of my nose if I don't respect my type declarations?
Upvotes: 2
Views: 1152
Reputation: 1446
Here are a couple of examples with safety 0
in sbcl:
CL-USER> (proclaim '(optimize (safety 0)))
; No value
CL-USER> (loop for i from 0 to 20
do (print (aref #(0) i)))
0
0
#(0)
(I)
I
NIL
(AREF #(0) I)
NIL
(PRINT (AREF #(0) I))
NIL
#<unknown immediate object, lowtag=#b1001, widetag=#x59 {D59}>
#<SB-KERNEL:LAYOUT for SB-KERNEL:LEXENV {10005F3C33}>
NIL
NIL
NIL
NIL
NIL
NIL
NIL
NIL
NIL
NIL
CL-USER> (defun f (x y)
(declare (fixnum x y))
(+ x y))
F
CL-USER> (f t ())
537919538
In SBCL if you smell nasal demons you can restart the image and input (sb-ext:restrict-compiler-policy 'safety 3)
(and maybe again with 'debug
, too) before loading the problematic code. With a little luck that will get you a nice condition instead of undefined behavior.
Upvotes: 2
Reputation: 139251
The Common Lisp standard says that there are type declarations. It does not really say what they do or what an implementation would do with them.
Purposes:
Let's say we have the operation:
(+ i 100)
By default it would be a generic +
, which can handle all numeric types.
If we tell that i
is a fixnum
then
+
operation can be fixnum specificIf additionally the return type is declared as a fixnum
:
If we additional tell the compiler that we want low safety
, then the compiler will not generate runtime type checks.
What capabilities the compiler provides is not standardized. It can even fully ignore the type declaration.
If you have a compiler which supports type specific code (and there are many such compilers), safety is low and one provides an object of the wrong type at runtime, then it can have unwanted consequences. Including crashing the Lisp due to heap memory corruption.
Thus it is best to use low safety only on very small code regions and not on whole files or systems. Using declarations with locally
helps.
SBCL (also SCL and CMUCL) is special, because it also treats type declarations as assertions for type checking.
Upvotes: 4
Reputation: 60004
Not sure about the demons, but you can get a segfault, as I did 15+ years ago with CMUCL and something similar to this code:
(declaim (optimize (speed 3) (safety 0)))
(defun f (v)
(declare (type (vector double-float) v))
(loop for x in v sum x))
(f #(1 2 3))
Note that I promised the I will only pass (vector double-float)
to f
and then I gave it a simple-vector
with fixnum
s.
Upvotes: 2