Types 常见的Lisp类型注释会导致不健康的行为吗?
我知道,如果安全设置较低,Common Lisp可以使用类型注释作为优化辅助工具,并且不会进行检查。例如,此程序运行并打印数字和字符串,没有任何类型错误。(当安全>=1时,我仅在SBCL中获得类型错误) 我现在想知道,如果安全设置为零并且不尊重类型注释,是否有可能创建一个做坏事的程序。比如从一种类型转换到另一种类型(比如C类型转换)以及其他类型的未定义行为Types 常见的Lisp类型注释会导致不健康的行为吗?,types,common-lisp,declare,Types,Common Lisp,Declare,我知道,如果安全设置较低,Common Lisp可以使用类型注释作为优化辅助工具,并且不会进行检查。例如,此程序运行并打印数字和字符串,没有任何类型错误。(当安全>=1时,我仅在SBCL中获得类型错误) 我现在想知道,如果安全设置为零并且不尊重类型注释,是否有可能创建一个做坏事的程序。比如从一种类型转换到另一种类型(比如C类型转换)以及其他类型的未定义行为 (declaim (optimize (speed 3) (safety 0)
(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))
到目前为止,我还没有找到一个这样的例子。我尝试了这个示例中使用类型化数组的变体,但都没有导致类型转换行为
(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中,程序会因简单类型错误而中止
如果我不尊重我的类型声明,有没有一种方法可以创建一个通用的Lisp程序,导致恶魔从我鼻子里冒出来?不确定恶魔是什么,但是你可以得到一个segfault,就像我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))
请注意,我承诺我只会将
(向量双浮点)
传递给f
,然后我用fixnum
s给它一个简单向量。不确定恶魔,但你可以得到一个segfault,就像我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))
请注意,我承诺只将(向量双浮点)
传递给f
,然后我用fixnum
s给它一个简单向量。它并没有真正说明它们做了什么,或者实现会对它们做什么
目的:
- 生成特定于类型的代码
- 运行时和/或编译时类型检查。这主要是由CMUCL、SBCL和SCL完成的
假设我们有了手术:
(+ i 100)
默认情况下,它将是一个通用的+
,可以处理所有数字类型
如果我们告诉i
是fixnum
,那么
+
操作可以是特定于fixnum的
如果另外将返回类型声明为fixnum
:
- 它可能不会溢出到bignum中
如果我们告诉编译器我们需要低安全性
,那么编译器将不会生成运行时类型检查
编译器提供的功能没有标准化。它甚至可以完全忽略类型声明
如果您有一个支持特定类型代码的编译器(并且有许多这样的编译器),那么安全性很低,并且一个编译器在运行时提供了错误类型的对象,那么它可能会产生不必要的后果。包括由于堆内存损坏而导致Lisp崩溃
因此,最好仅在非常小的代码区域上使用低安全性,而不是在整个文件或系统上使用低安全性。将声明与本地一起使用
会有所帮助
SBCL(也称为SCL和CMUCL)是特殊的,因为它还将类型声明视为用于类型检查的断言。公共Lisp标准说存在类型声明。它并没有真正说明它们做了什么,或者实现会对它们做什么
目的:
- 生成特定于类型的代码
- 运行时和/或编译时类型检查。这主要是由CMUCL、SBCL和SCL完成的
假设我们有了手术:
(+ i 100)
默认情况下,它将是一个通用的+
,可以处理所有数字类型
如果我们告诉i
是fixnum
,那么
+
操作可以是特定于fixnum的
如果另外将返回类型声明为fixnum
:
- 它可能不会溢出到bignum中
如果我们告诉编译器我们需要低安全性
,那么编译器将不会生成运行时类型检查
编译器提供的功能没有标准化。它甚至可以完全忽略类型声明
如果您有一个支持特定类型代码的编译器(并且有许多这样的编译器),那么安全性很低,并且一个编译器在运行时提供了错误类型的对象,那么它可能会产生不必要的后果。包括由于堆内存损坏而导致Lisp崩溃
因此,最好仅在非常小的代码区域上使用低安全性,而不是在整个文件或系统上使用低安全性。将声明与本地一起使用
会有所帮助
SBCL(也称为SCL和CMUCL)是特殊的,因为它还将类型声明视为类型检查的断言。这里有几个SBCL中的安全0
示例:
CL-USER> (proclaim '(optimize (safety 0)))
; No value
越界阅读
在SBCL中,如果你闻到鼻魔的气味,你可以在加载有问题的代码之前重新启动图像并输入(sb ext:restrict compiler policy'safety 3)
(也可以使用'debug
)。幸运的是,这将为您带来一个良好的条件,而不是未定义的行为。这里有几个sbcl中的safety 0
示例:
(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))
CL-USER> (proclaim '(optimize (safety 0)))
; No value
越界阅读
在SBCL中,如果你闻到鼻魔的气味,你可以在加载有问题的代码之前重新启动图像并输入(sb ext:restrict compiler policy'safety 3)
(也可以使用'debug
)。如果运气好的话,这将为您提供一个良好的条件,而不是未定义的行为。我不知道这是否是恶魔,但在CCL中,您的程序会打印:a1 6614253635515
。对于SBCL,类型推断知道本地创建值的类型。sds的答案显示了解决这个问题的好方法。@Xach:你能举个例子吗?在SBCL上运行sds的示例会给我一个#(1 2 3)不是列表错误,而是我想要的无意义值。如果这是一个恶魔,我不知道。但是在CCL中,您的程序会打印:a1 6614253635515
。对于SBCL,键入
(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))