我知道如果安全设置很低,Common Lisp可以使用类型注释作为优化辅助工具而不会被检查。例如,此程序运行并打印数字和字符串,没有任何类型错误。 (当安全> = 1时,我只在SBCL中出现类型错误)
(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"))
我现在想知道如果安全性设置为零并且不遵守类型注释,是否可以创建一个令人讨厌的程序。比如从一种类型转换为另一种类型(如C类型转换)和其他类型的未定义行为。
到目前为止,我还没有找到一个能够做到这一点的例子。我尝试了使用类型化数组的此示例的变体,但没有一个导致类型转换行为。
(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))
在CLISP中,此程序打印" hello",而不对int进行类型转换,在SBCL中,程序以SIMPLE-TYPE-ERROR错误中止。
有没有办法创建一个Common Lisp程序,如果我不尊重我的类型声明,会导致恶魔从我的鼻子中冒出来?
答案 0 :(得分:4)
Common Lisp标准说有类型声明。它并没有真正说出他们做了什么或者实现对他们做了什么。
用途:
假设我们有操作:
(+ i 100)
默认情况下,它是一个通用+
,可以处理所有数字类型。
如果我们告诉i
是fixnum
那么
+
操作可以是特定于fixnum的如果另外将返回类型声明为fixnum
:
如果我们另外告诉编译器我们想要低safety
,那么编译器将不会生成运行时类型检查。
编译器提供的功能未标准化。它甚至可以完全忽略类型声明。
如果你有一个支持类型特定代码的编译器(并且有许多这样的编译器),安全性很低,并且在运行时提供了错误类型的对象,那么它可能会产生不良后果。包括因堆内存损坏而崩溃Lisp。
因此,最好只在非常小的代码区域使用低安全性,而不是在整个文件或系统上使用。使用locally
声明有助于。
SBCL(也是SCL和CMUCL)很特殊,因为它还将类型声明视为类型检查的断言。
答案 1 :(得分:2)
不确定恶魔,但是你可以获得段错误,就像我15年前用CMUCL做的那样和类似的代码:
(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))
请注意,我承诺我只会将(vector double-float)
传递给f
,然后我用simple-vector
给它fixnum
。
答案 2 :(得分:2)
以下是sbcl中的safety 0
的几个示例:
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
在SBCL中,如果您闻到鼻子恶魔,您可以重新启动图像并输入(sb-ext:restrict-compiler-policy 'safety 3)
(也可能再次使用'debug
),然后再加载有问题的代码。运气不错会让你得到一个很好的条件,而不是未定义的行为。