带&key和&body参数的宏未指定key参数的SBCL编译问题
SBCL编译宏时的函数对象转储错误原因分析
宏定义与问题场景
先看涉事宏的定义:
(defparameter *current-state* 'foo) (defmacro when-state ((state &key (test #'eq)) &body body) `(when (funcall ,test *current-state* ,state) ,@body))
该宏在REPL环境下无论是否指定test关键字参数都能正常运行,但在顶层defun或lambda中使用时(如下示例),用SBCL编译文件会触发错误:
示例代码:
(defun foo () (when-state ('foo) (format t "Foo")))
编译错误信息
编译时SBCL输出的错误内容:
; in: DEFUN FOO ; (SENTO.FSM:WHEN-STATE ('SENTO.FSM::FOO) ; (FORMAT T "Foo")) ; --> IF FUNCALL ; ==> ; 1 ; ; caught ERROR: ; Objects of type COMPILED-FUNCTION can't be dumped into fasl files. ; ; note: The first argument never returns a value.
问题核心原因
问题出在宏的默认参数(test #'eq)上:
- 当定义宏时,
#'eq会被立即求值为当前环境下的已编译函数对象,而非符号引用。 - 当宏展开到
defun的代码中时,这个已编译的函数对象会被直接嵌入到生成的代码里。 - SBCL的FASL编译文件无法存储已编译的函数对象,只能存储符号(加载时通过符号查找对应函数),因此编译时会报错。
而显式指定test参数时(比如(when-state ('foo :test #'eq))),#'eq是在函数foo的上下文里被引用,编译时会生成对符号eq的引用,而非直接嵌入函数对象,因此可以正常转储到FASL文件。
修复方法
把宏默认参数里的#'eq改成符号引用'eq,确保宏展开后引用的是符号而非已编译函数对象:
(defmacro when-state ((state &key (test 'eq)) &body body) `(when (funcall ,test *current-state* ,state) ,@body))
这样无论是否显式指定test参数,编译时都会处理为符号引用,避免无法转储的问题。
内容的提问来源于stack exchange,提问作者Manfred
相关产品推荐
相关产品推荐

