等价于执行
(defmacro boo (x)
`(boo ,x))
(defun foo (x)
(boo x))
正解,zsbd
我是不是该反过来考考你,为什么用 defsubst
不会卡住?
cl-defsubst
由于会首先定义 compiler macro
而导致最后的 cl-defun
在 macroexpand-all
时会出现宏的无限展开,但 defsubst
仅在 byte-compile 生成字节码时起作用。
(所以无限循环会发生在调用 byte-compile
时。
你说的大部分都对,不过严格来说最后导致 black hole 的 compiler macroexpand 是由 defalias
来完成的,也就是 Emacs 为了支持 cl compiler macro 在解释器上开了洞。
有意思
找个时间看看去
这东西有点黑。
没记错的话,cl-defsubst
是 cl-lib
的遗留产物,是利用 compiler macro 来工作的,正常来说不应该使用。
如果真的需要 compiler macro 重写的话,可以自己写 compiler macro 实现,或者用 define-inline
。
而且 cl-defsubst
的实现其实有漏洞,inline.el
里就举了这么一个例子
;; - cl-defsubst: only works by accident, since it has latent bugs in its
;; handling of variables and scopes which could bite you at any time.
;; (e.g. try (cl-defsubst my-test1 (x) (let ((y 5)) (+ x y)))
;; and then M-: (macroexpand-all '(my-test1 y)) RET)
很早之前就看过你写的那篇 浅析Elisp中的compiler macro,一年前我写一个关于在 Emacs 中使用内联函数的总结的时候很有帮助。在测试 cl-defsubst
的时候,和你在帖子里描述的错误一致。那时候我用的是 Emacs 28.2。
不过这个 bug 在这个 commit 中被修复了: Faster and less wrong cl-defsubst inlining,现在 cl-defsubst
也能正常展开了。下面分别是我在 Emacs 29.2 和 Emacs 30.0.50 中得到的结果:
;; 29.2
(cl-defsubst my-test1 (x) (let ((y 5)) (+ x y)))
(macroexpand-all '(my-test1 y))
⇒ (let ((y 5)) (+ y y))
;; 30.0.50
(cl-defsubst my-test1 (x) (let ((y 5)) (+ x y)))
(macroexpand-all '(my-test1 y))
⇒ (let* ((x y)) (let ((y 5)) (+ x y)))