Common Lisp 类型注释会导致不正常的行为吗?
Can Common Lisp type annotations result in unsound behavior?
我知道如果安全设置低,Common Lisp 可以使用类型注释作为优化辅助,并且不会被检查。例如,此程序运行并打印数字和字符串,没有任何类型错误。 (当 safety >= 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 类型转换)和其他类型的未定义行为。
到目前为止,我还没有找到这样做的例子。我尝试了使用类型化数组的此示例的变体,但其中 none 导致了类型转换行为。
(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 程序,如果我不遵守我的类型声明,它就会让恶魔从我的鼻子里冒出来?
不确定这些恶魔,但是你可以遇到段错误,就像我 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
。
Common Lisp 标准说有类型声明。它并没有真正说明它们做什么或实现将对它们做什么。
用途:
- 生成类型特定代码
- 运行-time and/or 编译时类型检查。这主要是由 CMUCL、SBCL 和 SCL 完成的。
假设我们有操作:
(+ i 100)
默认情况下,它将是一个泛型 +
,它可以处理所有数字类型。
如果我们告诉 i
是一个 fixnum
那么
+
操作可以特定于 fixnum
如果另外将 return 类型声明为 fixnum
:
- 它可能不会溢出到一个 bignum
如果我们额外告诉编译器我们想要低 safety
,那么编译器将不会生成 运行time 类型检查。
编译器提供的功能没有标准化。它甚至可以完全忽略类型声明。
如果你有一个支持特定类型代码的编译器(并且有很多这样的编译器),安全性很低并且在 运行 时提供了错误类型的对象,那么它可能会产生意想不到的后果.包括由于堆内存损坏导致 Lisp 崩溃。
因此,最好只在非常小的代码区域而不是整个文件或系统上使用低安全性。使用带有 locally
的声明会有所帮助。
SBCL(也称为 SCL 和 CMUCL)比较特殊,因为它也将类型声明视为类型检查的断言。
以下是 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
)。运气好的话,你会得到一个不错的条件,而不是未定义的行为。
我知道如果安全设置低,Common Lisp 可以使用类型注释作为优化辅助,并且不会被检查。例如,此程序运行并打印数字和字符串,没有任何类型错误。 (当 safety >= 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 类型转换)和其他类型的未定义行为。
到目前为止,我还没有找到这样做的例子。我尝试了使用类型化数组的此示例的变体,但其中 none 导致了类型转换行为。
(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 程序,如果我不遵守我的类型声明,它就会让恶魔从我的鼻子里冒出来?
不确定这些恶魔,但是你可以遇到段错误,就像我 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
。
Common Lisp 标准说有类型声明。它并没有真正说明它们做什么或实现将对它们做什么。
用途:
- 生成类型特定代码
- 运行-time and/or 编译时类型检查。这主要是由 CMUCL、SBCL 和 SCL 完成的。
假设我们有操作:
(+ i 100)
默认情况下,它将是一个泛型 +
,它可以处理所有数字类型。
如果我们告诉 i
是一个 fixnum
那么
+
操作可以特定于 fixnum
如果另外将 return 类型声明为 fixnum
:
- 它可能不会溢出到一个 bignum
如果我们额外告诉编译器我们想要低 safety
,那么编译器将不会生成 运行time 类型检查。
编译器提供的功能没有标准化。它甚至可以完全忽略类型声明。
如果你有一个支持特定类型代码的编译器(并且有很多这样的编译器),安全性很低并且在 运行 时提供了错误类型的对象,那么它可能会产生意想不到的后果.包括由于堆内存损坏导致 Lisp 崩溃。
因此,最好只在非常小的代码区域而不是整个文件或系统上使用低安全性。使用带有 locally
的声明会有所帮助。
SBCL(也称为 SCL 和 CMUCL)比较特殊,因为它也将类型声明视为类型检查的断言。
以下是 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
)。运气好的话,你会得到一个不错的条件,而不是未定义的行为。