在 SBCL 的源代码中y-or-n-p,我看到(declare (explicit-check)):
(defun y-or-n-p (&optional format-string &rest arguments)
"..."
(declare (explicit-check))
(flet ((print-query ()
(apply #'maybe-print-query "(y or n)" format-string arguments)))
(loop (print-query)
(case (query-read-char)
((#\y #\Y) (return t))
((#\n #\N) (return nil))
(t (clarify-legal-query-input "y" "n"))))))
Run Code Online (Sandbox Code Playgroud)
有什么explicit-check作用?它没有在HyperSpec中列为标准声明标识符,因此它可能是实现定义的。explicit-check但是,我在SBCL 手册中没有看到任何提及。
根据代码中的以下来源,似乎explicit-check表明与声明所声明的函数参数关联的类型FTYPE不会自动检查,而是显式(手动)检查。
f当某些函数将一个参数的类型分派给其签名由 声明的a专用函数时,这可以避免进行冗余检查。如果没有in , 的类型将被检查两次,一次在类型分派期间,一次在进入函数时。f_aFTYPEexplicit-checkf_aa
src/compiler/ir1-translators.lisp
;;; Check a new global function definition for consistency with
;;; previous declaration or definition, and assert argument/result
;;; types if appropriate. This assertion is suppressed by the
;;; EXPLICIT-CHECK attribute, which is specified on functions that
;;; check their argument types as a consequence of type dispatching.
;;; This avoids redundant checks such as NUMBERP on the args to +, etc.
;;; FIXME: this seems to have nothing at all to do with adding "new"
;;; definitions, as it is only called from IR1-CONVERT-INLINE-EXPANSION.
Run Code Online (Sandbox Code Playgroud)
src/compiler/ctype.lisp
(warn "Explicit-check without known FTYPE is meaningless")
Run Code Online (Sandbox Code Playgroud)
package-data-list.lisp-expr
;; Advice to the compiler that it doesn't need to assert types.
"EXPLICIT-CHECK"
Run Code Online (Sandbox Code Playgroud)
src/compiler/ir1tran.lisp
;; EXPLICIT-CHECK by itself specifies that all argument and
;; result types are checked by the function body.
Run Code Online (Sandbox Code Playgroud)
在 的上下文中y-or-n-p,目的是仅检查类型一次。它可以尽早完成,例如在 的开头y-or-n-p,然后调用不检查其类型的“不安全”函数,但这里不是这种情况。
相反,该函数是用 定义的defknown,它执行以下代码:
(setf (info :function :type name) type-to-store)
Run Code Online (Sandbox Code Playgroud)
(看src/compiler/knownfun.lisp)
如果我没有记错的话,这与FTYPE为函数声明(ftype并defknown设置此信息槽)具有相同的效果。
但是,这里y-or-n-p不需要检查类型本身,因为它主要委托给另一个函数,即maybe-print-query. 该函数调用format,也已声明explicit-check。
它做的第一件事是etypecheck目标参数,以便将对 format 的调用分派到 的不同调用,这又根据下一个参数的类型(控制字符串或格式化函数)分支到不同的结果。%format
因此在实践中,检查参数的类型与调度是交织在一起的,因为有很多极端情况需要考虑。
首先检查输入类型然后传递给不安全函数的自顶向下方法也需要执行一些复杂的类型检查步骤,然后仍然需要根据参数类型进行分派。这可能就是类型检查被延迟直到代码到达不同特殊情况的原因。